bitkeeper revision 1.154.1.1 (3e7f35e65e93xTR77zgPWlou5QJAgw)
authorjws@cairnwell.research <jws@cairnwell.research>
Mon, 24 Mar 2003 16:44:22 +0000 (16:44 +0000)
committerjws@cairnwell.research <jws@cairnwell.research>
Mon, 24 Mar 2003 16:44:22 +0000 (16:44 +0000)
Added aic7xxx support
Included linux string library in place of cut down lib.{c,h} versions
Added missing strtoul prototype in lib.h

53 files changed:
.rootkeys
BitKeeper/etc/logging_ok
xen/common/lib.c
xen/common/string.c [new file with mode: 0644]
xen/drivers/scsi/Makefile
xen/drivers/scsi/aic7xxx/Makefile [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7770.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7770_osm.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic79xx.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic79xx.reg [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic79xx.seq [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic79xx_core.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic79xx_host.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic79xx_inline.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic79xx_osm.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic79xx_osm.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic79xx_osm_pci.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic79xx_pci.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic79xx_proc.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic79xx_reg.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic79xx_seq.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx.reg [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx.seq [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx_core.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx_host.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx_inline.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx_osm.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx_osm.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx_pci.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx_proc.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx_reg.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aic7xxx_seq.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aicasm/Makefile [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aicasm/aicasm.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aicasm/aicasm.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/cam.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/queue.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/scsi_iu.h [new file with mode: 0644]
xen/drivers/scsi/aic7xxx/scsi_message.h [new file with mode: 0644]
xen/include/asm-i386/string.h [new file with mode: 0644]
xen/include/xeno/lib.h
xen/include/xeno/string.h [new file with mode: 0644]

index 99803cc8e710c3bc6a03d6dc67958cd950682c40..56195deb5cdb0e76872b772b1cdb87c5bdd8e4fb 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 3e397e6619PgAfBbw2XFbXkewvUWgw xen/common/schedule.c
 3ddb79bdB9RNMnkQnUyZ5C9hhMSQQw xen/common/slab.c
 3ddb79bd0gVQYmL2zvuJnldvD0AGxQ xen/common/softirq.c
+3e7f358awXBC3Vw-wFRwPw18qL1khg xen/common/string.c
 3ddb79bdQqFHtHRGEO2dsxGgo6eAhw xen/common/timer.c
 3ddb79bd3zgV33PHdt-cgh3sxcb1hw xen/common/vsprintf.c
 3ddb79c0ppNeJtjC4va8j41ADCnchA xen/drivers/Makefile
 3e564137taxQbEVa39h3mEVLFxRydQ xen/drivers/scsi/aacraid/linit.c
 3e564137vmALxpfK7vNINqEklSmQ1A xen/drivers/scsi/aacraid/rx.c
 3e564137EuYUJgvqnOunPERWxnp_mw xen/drivers/scsi/aacraid/sa.c
+3e7f358amV8HBe1Cyges2wsBHfh-cw xen/drivers/scsi/aic7xxx/Makefile
+3e7f358al_wJbBvERr7NzW5mLuTRpQ xen/drivers/scsi/aic7xxx/aic7770.c
+3e7f358aCCSVBMcmsh64S7CCVIgVQg xen/drivers/scsi/aic7xxx/aic7770_osm.c
+3e7f358aLNpSseMxk_DY6a-Ln9OyGg xen/drivers/scsi/aic7xxx/aic79xx.h
+3e7f358ad7OR21rmyDNJfKyE1X1sxg xen/drivers/scsi/aic7xxx/aic79xx.reg
+3e7f358a9Z39hG2enFVrNWjizvwthg xen/drivers/scsi/aic7xxx/aic79xx.seq
+3e7f358aKzcs8L8XYcLD0bF8FrmtVg xen/drivers/scsi/aic7xxx/aic79xx_core.c
+3e7f358ayrUAqJ9b8GxdRJQr5Po1zg xen/drivers/scsi/aic7xxx/aic79xx_host.h
+3e7f358aVDB0b6qAf__XSDPubVM1RA xen/drivers/scsi/aic7xxx/aic79xx_inline.h
+3e7f358aQFjWLEqUzd98Gy8z86vk2g xen/drivers/scsi/aic7xxx/aic79xx_osm.c
+3e7f358adsO2cSMKATR_r9hPZ9KCUg xen/drivers/scsi/aic7xxx/aic79xx_osm.h
+3e7f358avC9liKFAUFFRyWlVIIGZ2g xen/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
+3e7f358aQ0ZpnmwtiT16raQb1QSGKQ xen/drivers/scsi/aic7xxx/aic79xx_pci.c
+3e7f358af6BTct89-TCB31Rtw8NzQg xen/drivers/scsi/aic7xxx/aic79xx_proc.c
+3e7f358aK54-KV7Wj014Yf5FnWo2VQ xen/drivers/scsi/aic7xxx/aic79xx_reg.h
+3e7f358aZiCJAqEBttlmEDCMFcM-aA xen/drivers/scsi/aic7xxx/aic79xx_seq.h
+3e7f358aPZhHGnMNAM3YCQcBIAnCfw xen/drivers/scsi/aic7xxx/aic7xxx.h
+3e7f358aZRO8yoMXVLyDUyoFhsFnBw xen/drivers/scsi/aic7xxx/aic7xxx.reg
+3e7f358aADKO4MLsWISX0-d2hpru0g xen/drivers/scsi/aic7xxx/aic7xxx.seq
+3e7f358aB5x1ljQE7yWp63nvcfp4Mg xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
+3e7f358aDCmrU2Q35Ay765z7gWTSmQ xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.h
+3e7f358aXOqteLTeEH8XOlAeLPbXuQ xen/drivers/scsi/aic7xxx/aic7xxx_core.c
+3e7f358adzSPC6RFtP4NyHB2PoT-Ew xen/drivers/scsi/aic7xxx/aic7xxx_host.h
+3e7f358asCAiwyDZ1fBaiIxABh30BA xen/drivers/scsi/aic7xxx/aic7xxx_inline.h
+3e7f358aDwj6BY9tJ4spPuZnQqBiew xen/drivers/scsi/aic7xxx/aic7xxx_osm.c
+3e7f358anOWiWoFl7baNMWjqsPiYzg xen/drivers/scsi/aic7xxx/aic7xxx_osm.h
+3e7f358a1sRjg-XkczIXH7dUX8K1ZA xen/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
+3e7f358aj6uST9miAVmB9WNLqAGkCA xen/drivers/scsi/aic7xxx/aic7xxx_pci.c
+3e7f358aCwRu3fI3NP7MQsLRpWV73w xen/drivers/scsi/aic7xxx/aic7xxx_proc.c
+3e7f358aW8xbofV6VW2L8FPphmAXvg xen/drivers/scsi/aic7xxx/aic7xxx_reg.h
+3e7f358ayQSlIZBIAGWYXBM5dtAFrA xen/drivers/scsi/aic7xxx/aic7xxx_seq.h
+3e7f358acS9V8TbKLuyxvw-0SlgYmQ xen/drivers/scsi/aic7xxx/aicasm/Makefile
+3e7f358arkcPw6k1oC2Wrp5TXFbovg xen/drivers/scsi/aic7xxx/aicasm/aicasm.c
+3e7f358aF_JURnGn6q5V951k1TTnLg xen/drivers/scsi/aic7xxx/aicasm/aicasm.h
+3e7f358a2aopdml6zsS1HF9oibF5RQ xen/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
+3e7f358a2JQOz8ASFfi2oV5-k127RA xen/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
+3e7f358a5kU_nwzMFh0xbG96yLgJKA xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y
+3e7f358a4FjmeDBfwgynM1InqFQ2nA xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
+3e7f358aoXDwp6vozr1x2Fwvb7x5BQ xen/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
+3e7f358aUYJHOx790PiwQiJIlYECbA xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
+3e7f358aPn8RK1slOgqrXC12x0FlgQ xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
+3e7f358a6BujWXsqDmuts6N_A8dGhw xen/drivers/scsi/aic7xxx/cam.h
+3e7f358anzaPhN49caB9AqMVw0_XKQ xen/drivers/scsi/aic7xxx/queue.h
+3e7f358a3ggaLCNsnuArANSmNT1t2A xen/drivers/scsi/aic7xxx/scsi_iu.h
+3e7f358arKN4VFRDcEasaM6Wj_VwTg xen/drivers/scsi/aic7xxx/scsi_message.h
 3e56411aOLzeD5CRbuXquOrX0lRijw xen/drivers/scsi/constants.c
 3ddb79beXZxwKh7cGyPfr40bhDyRrA xen/drivers/scsi/constants.h
 3e564120ZeinH9nf3IVSerB80T7dHg xen/drivers/scsi/hosts.c
 3ddb79c3jn8ALV_S9W5aeTYUQRKBpg xen/include/asm-i386/smpboot.h
 3ddb79c3e9DCEoR-WzNxcOQDzLu7BQ xen/include/asm-i386/softirq.h
 3ddb79c3NiyQE2vQnyGiaBnNjBO1rA xen/include/asm-i386/spinlock.h
+3e7f358aG11EvMI9VJ4_9hD4LUO7rQ xen/include/asm-i386/string.h
 3ddb79c3ezddh34MdelJpa5tNR00Dw xen/include/asm-i386/system.h
 3e397e66xPNc8eaSqC9pPbyAtRGzHA xen/include/asm-i386/time.h
 3e450943TfE-iovQIY_tMO_VdGsPhA xen/include/asm-i386/timex.h
 3ddb79c1-yIt89RT02wIPp2xDR8YjQ xen/include/xeno/socket.h
 3ddb79c2V2P9F2xMCzDJ9vbUofSg_Q xen/include/xeno/sockios.h
 3ddb79c2iIcESrDAB8samy_yAh6olQ xen/include/xeno/spinlock.h
+3e7f358aMtFMUVvN_Zjg5qvEJIqEBA xen/include/xeno/string.h
 3ddb79c0BnA20PbgmuMPSGIBljNRQw xen/include/xeno/time.h
 3ddb79c2HFkXuRxi1CriJtSFmY6Ybw xen/include/xeno/timer.h
 3ddb79c2_m8lT9jDKse_tePj7zcnNQ xen/include/xeno/timex.h
index 80c0db8f9a6ea3a48a188a702c9472d40d3def1a..b6e8d009bf44607d80ac7e63a3f3546723a27773 100644 (file)
@@ -5,6 +5,7 @@ akw27@plucky.localdomain
 bd240@boulderdash.cl.cam.ac.uk
 bd240@labyrinth.cl.cam.ac.uk
 iap10@labyrinth.cl.cam.ac.uk
+jws@cairnwell.research
 kaf24@labyrinth.cl.cam.ac.uk
 kaf24@plym.cl.cam.ac.uk
 kaf24@scramble.cl.cam.ac.uk
index ae75196ffdce5398e2133e388473346d69fa77cf..c871341b7f5ecad99da7fdd9264f56b3652bf12a 100644 (file)
@@ -2,6 +2,7 @@
 #include <xeno/ctype.h>
 #include <xeno/lib.h>
 
+#if 0 // jws - now in string.c, string.h, asm/string.h 
 int memcmp(const void * cs,const void * ct,size_t count)
 {
        const unsigned char *su1, *su2;
@@ -119,7 +120,7 @@ char * strstr(const char * s1,const char * s2)
         }
         return NULL;
 }
-
+#endif
 
 /* for inc/ctype.h */
 unsigned char _ctype[] = {
diff --git a/xen/common/string.c b/xen/common/string.c
new file mode 100644 (file)
index 0000000..2ff4efc
--- /dev/null
@@ -0,0 +1,535 @@
+/*
+ *  linux/lib/string.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ *
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
+ * -  Added strsep() which will replace strtok() soon (because strsep() is
+ *    reentrant and should be faster). Use only strsep() in new code, please.
+ */
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+
+#ifndef __HAVE_ARCH_STRNICMP
+/**
+ * strnicmp - Case insensitive, length-limited string comparison
+ * @s1: One string
+ * @s2: The other string
+ * @len: the maximum number of characters to compare
+ */
+int strnicmp(const char *s1, const char *s2, size_t len)
+{
+       /* Yes, Virginia, it had better be unsigned */
+       unsigned char c1, c2;
+
+       c1 = 0; c2 = 0;
+       if (len) {
+               do {
+                       c1 = *s1; c2 = *s2;
+                       s1++; s2++;
+                       if (!c1)
+                               break;
+                       if (!c2)
+                               break;
+                       if (c1 == c2)
+                               continue;
+                       c1 = tolower(c1);
+                       c2 = tolower(c2);
+                       if (c1 != c2)
+                               break;
+               } while (--len);
+       }
+       return (int)c1 - (int)c2;
+}
+#endif
+
+char * ___strtok;
+
+#ifndef __HAVE_ARCH_STRCPY
+/**
+ * strcpy - Copy a %NUL terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ */
+char * strcpy(char * dest,const char *src)
+{
+       char *tmp = dest;
+
+       while ((*dest++ = *src++) != '\0')
+               /* nothing */;
+       return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNCPY
+/**
+ * strncpy - Copy a length-limited, %NUL-terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ * @count: The maximum number of bytes to copy
+ *
+ * Note that unlike userspace strncpy, this does not %NUL-pad the buffer.
+ * However, the result is not %NUL-terminated if the source exceeds
+ * @count bytes.
+ */
+char * strncpy(char * dest,const char *src,size_t count)
+{
+       char *tmp = dest;
+
+       while (count-- && (*dest++ = *src++) != '\0')
+               /* nothing */;
+
+       return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRCAT
+/**
+ * strcat - Append one %NUL-terminated string to another
+ * @dest: The string to be appended to
+ * @src: The string to append to it
+ */
+char * strcat(char * dest, const char * src)
+{
+       char *tmp = dest;
+
+       while (*dest)
+               dest++;
+       while ((*dest++ = *src++) != '\0')
+               ;
+
+       return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNCAT
+/**
+ * strncat - Append a length-limited, %NUL-terminated string to another
+ * @dest: The string to be appended to
+ * @src: The string to append to it
+ * @count: The maximum numbers of bytes to copy
+ *
+ * Note that in contrast to strncpy, strncat ensures the result is
+ * terminated.
+ */
+char * strncat(char *dest, const char *src, size_t count)
+{
+       char *tmp = dest;
+
+       if (count) {
+               while (*dest)
+                       dest++;
+               while ((*dest++ = *src++)) {
+                       if (--count == 0) {
+                               *dest = '\0';
+                               break;
+                       }
+               }
+       }
+
+       return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRCMP
+/**
+ * strcmp - Compare two strings
+ * @cs: One string
+ * @ct: Another string
+ */
+int strcmp(const char * cs,const char * ct)
+{
+       register signed char __res;
+
+       while (1) {
+               if ((__res = *cs - *ct++) != 0 || !*cs++)
+                       break;
+       }
+
+       return __res;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNCMP
+/**
+ * strncmp - Compare two length-limited strings
+ * @cs: One string
+ * @ct: Another string
+ * @count: The maximum number of bytes to compare
+ */
+int strncmp(const char * cs,const char * ct,size_t count)
+{
+       register signed char __res = 0;
+
+       while (count) {
+               if ((__res = *cs - *ct++) != 0 || !*cs++)
+                       break;
+               count--;
+       }
+
+       return __res;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRCHR
+/**
+ * strchr - Find the first occurrence of a character in a string
+ * @s: The string to be searched
+ * @c: The character to search for
+ */
+char * strchr(const char * s, int c)
+{
+       for(; *s != (char) c; ++s)
+               if (*s == '\0')
+                       return NULL;
+       return (char *) s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRRCHR
+/**
+ * strrchr - Find the last occurrence of a character in a string
+ * @s: The string to be searched
+ * @c: The character to search for
+ */
+char * strrchr(const char * s, int c)
+{
+       const char *p = s + strlen(s);
+       do {
+           if (*p == (char)c)
+               return (char *)p;
+       } while (--p >= s);
+       return NULL;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRLEN
+/**
+ * strlen - Find the length of a string
+ * @s: The string to be sized
+ */
+size_t strlen(const char * s)
+{
+       const char *sc;
+
+       for (sc = s; *sc != '\0'; ++sc)
+               /* nothing */;
+       return sc - s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNLEN
+/**
+ * strnlen - Find the length of a length-limited string
+ * @s: The string to be sized
+ * @count: The maximum number of bytes to search
+ */
+size_t strnlen(const char * s, size_t count)
+{
+       const char *sc;
+
+       for (sc = s; count-- && *sc != '\0'; ++sc)
+               /* nothing */;
+       return sc - s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRSPN
+/**
+ * strspn - Calculate the length of the initial substring of @s which only
+ *     contain letters in @accept
+ * @s: The string to be searched
+ * @accept: The string to search for
+ */
+size_t strspn(const char *s, const char *accept)
+{
+       const char *p;
+       const char *a;
+       size_t count = 0;
+
+       for (p = s; *p != '\0'; ++p) {
+               for (a = accept; *a != '\0'; ++a) {
+                       if (*p == *a)
+                               break;
+               }
+               if (*a == '\0')
+                       return count;
+               ++count;
+       }
+
+       return count;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRPBRK
+/**
+ * strpbrk - Find the first occurrence of a set of characters
+ * @cs: The string to be searched
+ * @ct: The characters to search for
+ */
+char * strpbrk(const char * cs,const char * ct)
+{
+       const char *sc1,*sc2;
+
+       for( sc1 = cs; *sc1 != '\0'; ++sc1) {
+               for( sc2 = ct; *sc2 != '\0'; ++sc2) {
+                       if (*sc1 == *sc2)
+                               return (char *) sc1;
+               }
+       }
+       return NULL;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRTOK
+/**
+ * strtok - Split a string into tokens
+ * @s: The string to be searched
+ * @ct: The characters to search for
+ *
+ * WARNING: strtok is deprecated, use strsep instead.
+ */
+char * strtok(char * s,const char * ct)
+{
+       char *sbegin, *send;
+
+       sbegin  = s ? s : ___strtok;
+       if (!sbegin) {
+               return NULL;
+       }
+       sbegin += strspn(sbegin,ct);
+       if (*sbegin == '\0') {
+               ___strtok = NULL;
+               return( NULL );
+       }
+       send = strpbrk( sbegin, ct);
+       if (send && *send != '\0')
+               *send++ = '\0';
+       ___strtok = send;
+       return (sbegin);
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRSEP
+/**
+ * strsep - Split a string into tokens
+ * @s: The string to be searched
+ * @ct: The characters to search for
+ *
+ * strsep() updates @s to point after the token, ready for the next call.
+ *
+ * It returns empty tokens, too, behaving exactly like the libc function
+ * of that name. In fact, it was stolen from glibc2 and de-fancy-fied.
+ * Same semantics, slimmer shape. ;)
+ */
+char * strsep(char **s, const char *ct)
+{
+       char *sbegin = *s, *end;
+
+       if (sbegin == NULL)
+               return NULL;
+
+       end = strpbrk(sbegin, ct);
+       if (end)
+               *end++ = '\0';
+       *s = end;
+
+       return sbegin;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMSET
+/**
+ * memset - Fill a region of memory with the given value
+ * @s: Pointer to the start of the area.
+ * @c: The byte to fill the area with
+ * @count: The size of the area.
+ *
+ * Do not use memset() to access IO space, use memset_io() instead.
+ */
+void * memset(void * s,int c,size_t count)
+{
+       char *xs = (char *) s;
+
+       while (count--)
+               *xs++ = c;
+
+       return s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_BCOPY
+/**
+ * bcopy - Copy one area of memory to another
+ * @src: Where to copy from
+ * @dest: Where to copy to
+ * @count: The size of the area.
+ *
+ * Note that this is the same as memcpy(), with the arguments reversed.
+ * memcpy() is the standard, bcopy() is a legacy BSD function.
+ *
+ * You should not use this function to access IO space, use memcpy_toio()
+ * or memcpy_fromio() instead.
+ */
+char * bcopy(const char * src, char * dest, int count)
+{
+       char *tmp = dest;
+
+       while (count--)
+               *tmp++ = *src++;
+
+       return dest;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMCPY
+/**
+ * memcpy - Copy one area of memory to another
+ * @dest: Where to copy to
+ * @src: Where to copy from
+ * @count: The size of the area.
+ *
+ * You should not use this function to access IO space, use memcpy_toio()
+ * or memcpy_fromio() instead.
+ */
+void * memcpy(void * dest,const void *src,size_t count)
+{
+       char *tmp = (char *) dest, *s = (char *) src;
+
+       while (count--)
+               *tmp++ = *s++;
+
+       return dest;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMMOVE
+/**
+ * memmove - Copy one area of memory to another
+ * @dest: Where to copy to
+ * @src: Where to copy from
+ * @count: The size of the area.
+ *
+ * Unlike memcpy(), memmove() copes with overlapping areas.
+ */
+void * memmove(void * dest,const void *src,size_t count)
+{
+       char *tmp, *s;
+
+       if (dest <= src) {
+               tmp = (char *) dest;
+               s = (char *) src;
+               while (count--)
+                       *tmp++ = *s++;
+               }
+       else {
+               tmp = (char *) dest + count;
+               s = (char *) src + count;
+               while (count--)
+                       *--tmp = *--s;
+               }
+
+       return dest;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMCMP
+/**
+ * memcmp - Compare two areas of memory
+ * @cs: One area of memory
+ * @ct: Another area of memory
+ * @count: The size of the area.
+ */
+/*
+int memcmp(const void * cs,const void * ct,size_t count)
+{
+       const unsigned char *su1, *su2;
+       int res = 0;
+
+       for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
+               if ((res = *su1 - *su2) != 0)
+                       break;
+       return res;
+}
+*/
+#endif
+
+#ifndef __HAVE_ARCH_MEMSCAN
+/**
+ * memscan - Find a character in an area of memory.
+ * @addr: The memory area
+ * @c: The byte to search for
+ * @size: The size of the area.
+ *
+ * returns the address of the first occurrence of @c, or 1 byte past
+ * the area if @c is not found
+ */
+void * memscan(void * addr, int c, size_t size)
+{
+       unsigned char * p = (unsigned char *) addr;
+
+       while (size) {
+               if (*p == c)
+                       return (void *) p;
+               p++;
+               size--;
+       }
+       return (void *) p;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRSTR
+/**
+ * strstr - Find the first substring in a %NUL terminated string
+ * @s1: The string to be searched
+ * @s2: The string to search for
+ */
+char * strstr(const char * s1,const char * s2)
+{
+       int l1, l2;
+
+       l2 = strlen(s2);
+       if (!l2)
+               return (char *) s1;
+       l1 = strlen(s1);
+       while (l1 >= l2) {
+               l1--;
+               if (!memcmp(s1,s2,l2))
+                       return (char *) s1;
+               s1++;
+       }
+       return NULL;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMCHR
+/**
+ * memchr - Find a character in an area of memory.
+ * @s: The memory area
+ * @c: The byte to search for
+ * @n: The size of the area.
+ *
+ * returns the address of the first occurrence of @c, or %NULL
+ * if @c is not found
+ */
+void *memchr(const void *s, int c, size_t n)
+{
+       const unsigned char *p = s;
+       while (n-- != 0) {
+               if ((unsigned char)c == *p++) {
+                       return (void *)(p-1);
+               }
+       }
+       return NULL;
+}
+
+#endif
index 5b480bdf539b90df221addfe619efaed72229cef..5753581b4703f9de9307fdd48079ed85371759af 100644 (file)
@@ -3,9 +3,11 @@ include $(BASEDIR)/Rules.mk
 
 default: $(OBJS)
        $(MAKE) -C aacraid
-       $(LD) -r -o driver.o $(OBJS) aacraid/aacraid.o
+       $(MAKE) -C aic7xxx
+       $(LD) -r -o driver.o $(OBJS) aacraid/aacraid.o aic7xxx/aic7xxx.o
 #      $(LD) -r -o driver.o $(OBJS) 
 
 clean:
        $(MAKE) -C aacraid clean
+       $(MAKE) -C aic7xxx clean
        rm -f *.o *~ core
diff --git a/xen/drivers/scsi/aic7xxx/Makefile b/xen/drivers/scsi/aic7xxx/Makefile
new file mode 100644 (file)
index 0000000..333cea5
--- /dev/null
@@ -0,0 +1,69 @@
+#
+# drivers/scsi/aic7xxx/Makefile
+#
+# Makefile for the Linux aic7xxx SCSI driver.
+#
+
+O_TARGET := aic7xxx_drv.o
+
+include $(BASEDIR)/Rules.mk
+#CFLAGS += -I$(BASEDIR)/drivers/scsi
+
+#list-multi    := aic7xxx.o aic79xx.o
+
+#obj-$(CONFIG_SCSI_AIC7XXX)    += aic7xxx.o
+#ifeq ($(CONFIG_PCI),y)
+#obj-$(CONFIG_SCSI_AIC79XX)    += aic79xx.o
+#endif
+
+#EXTRA_CFLAGS += -g
+
+# Platform Specific Files
+obj-aic7xxx = aic7xxx_osm.o aic7xxx_proc.o aic7770_osm.o
+#PCI Specific Platform Files
+#ifeq ($(CONFIG_PCI),y)
+obj-aic7xxx += aic7xxx_osm_pci.o
+#endif
+# Core Files
+obj-aic7xxx += aic7xxx_core.o aic7xxx_93cx6.o aic7770.o
+#PCI Specific Core Files
+#ifeq ($(CONFIG_PCI),y)
+obj-aic7xxx += aic7xxx_pci.o
+#endif
+
+# Platform Specific U320 Files
+#obj-aic79xx = aic79xx_osm.o 
+#obj-aic79xx += aic79xx_proc.o
+#obj-aic79xx += aic79xx_osm_pci.o
+# Core Files
+#obj-aic79xx += aic79xx_core.o
+#obj-aic79xx += aic79xx_pci.o
+
+default: aic7xxx.o
+
+aic7xxx_core.o: aic7xxx_seq.h
+$(obj-aic7xxx): aic7xxx_reg.h
+aic7xxx.o: aic7xxx_seq.h aic7xxx_reg.h $(obj-aic7xxx)
+       $(LD) $(LD_RFLAG) -r -o $@ $(obj-aic7xxx)
+
+aic79xx_core.o: aic79xx_seq.h
+$(obj-aic79xx): aic79xx_reg.h
+aic79xx.o: aic79xx_seq.h aic79xx_reg.h $(obj-aic79xx)
+       $(LD) $(LD_RFLAG) -r -o $@ $(obj-aic79xx)
+
+#ifeq ($(CONFIG_AIC7XXX_BUILD_FIRMWARE),y)
+#aic7xxx_seq.h aic7xxx_reg.h: aic7xxx.seq aic7xxx.reg aicasm/aicasm
+#      aicasm/aicasm -I. -r aic7xxx_reg.h -o aic7xxx_seq.h aic7xxx.seq
+#endif
+
+#ifeq ($(CONFIG_AIC79XX_BUILD_FIRMWARE),y)
+#aic79xx_seq.h aic79xx_reg.h: aic79xx.seq aic79xx.reg aicasm/aicasm
+#      aicasm/aicasm -I. -r aic79xx_reg.h -o aic79xx_seq.h aic79xx.seq
+#endif
+
+#aicasm/aicasm: aicasm/*.[chyl]
+#      $(MAKE) -C aicasm
+
+clean:
+       rm -f *.o *~ core
+
diff --git a/xen/drivers/scsi/aic7xxx/aic7770.c b/xen/drivers/scsi/aic7xxx/aic7770.c
new file mode 100644 (file)
index 0000000..045f54c
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * Product specific probe and attach routines for:
+ *     27/284X and aic7770 motherboard SCSI controllers
+ *
+ * Copyright (c) 1994-1998, 2000, 2001 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7770.c#21 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7770.c,v 1.1 2000/09/16 20:02:27 gibbs Exp $
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aic7xxx_93cx6.h>
+#endif
+
+#define ID_AIC7770     0x04907770
+#define ID_AHA_274x    0x04907771
+#define ID_AHA_284xB   0x04907756 /* BIOS enabled */
+#define ID_AHA_284x    0x04907757 /* BIOS disabled*/
+
+static int aha2840_load_seeprom(struct ahc_softc *ahc);
+static ahc_device_setup_t ahc_aic7770_VL_setup;
+static ahc_device_setup_t ahc_aic7770_EISA_setup;;
+static ahc_device_setup_t ahc_aic7770_setup;
+
+
+struct aic7770_identity aic7770_ident_table [] =
+{
+       {
+               ID_AHA_274x,
+               0xFFFFFFFF,
+               "Adaptec 274X SCSI adapter",
+               ahc_aic7770_EISA_setup
+       },
+       {
+               ID_AHA_284xB,
+               0xFFFFFFFE,
+               "Adaptec 284X SCSI adapter",
+               ahc_aic7770_VL_setup
+       },
+       /* Generic chip probes for devices we don't know 'exactly' */
+       {
+               ID_AIC7770,
+               0xFFFFFFFF,
+               "Adaptec aic7770 SCSI adapter",
+               ahc_aic7770_EISA_setup
+       }
+};
+const int ahc_num_aic7770_devs = NUM_ELEMENTS(aic7770_ident_table);
+
+struct aic7770_identity *
+aic7770_find_device(uint32_t id)
+{
+       struct  aic7770_identity *entry;
+       int     i;
+
+       for (i = 0; i < ahc_num_aic7770_devs; i++) {
+               entry = &aic7770_ident_table[i];
+               if (entry->full_id == (id & entry->id_mask))
+                       return (entry);
+       }
+       return (NULL);
+}
+
+int
+aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry, u_int io)
+{
+       u_long  l;
+       u_long  s;
+       int     error;
+       int     have_seeprom;
+       u_int   hostconf;
+       u_int   irq;
+       u_int   intdef;
+
+       error = entry->setup(ahc);
+       have_seeprom = 0;
+       if (error != 0)
+               return (error);
+
+       error = aic7770_map_registers(ahc, io);
+       if (error != 0)
+               return (error);
+
+       /*
+        * Before we continue probing the card, ensure that
+        * its interrupts are *disabled*.  We don't want
+        * a misstep to hang the machine in an interrupt
+        * storm.
+        */
+       ahc_intr_enable(ahc, FALSE);
+
+       ahc->description = entry->name;
+       error = ahc_softc_init(ahc);
+
+       error = ahc_reset(ahc);
+       if (error != 0)
+               return (error);
+
+       /* Make sure we have a valid interrupt vector */
+       intdef = ahc_inb(ahc, INTDEF);
+       irq = intdef & VECTOR;
+       switch (irq) {
+       case 9:
+       case 10:
+       case 11:
+       case 12:
+       case 14:
+       case 15:
+               break;
+       default:
+               printf("aic7770_config: illegal irq setting %d\n", intdef);
+               return (ENXIO);
+       }
+
+       if ((intdef & EDGE_TRIG) != 0)
+               ahc->flags |= AHC_EDGE_INTERRUPT;
+
+       switch (ahc->chip & (AHC_EISA|AHC_VL)) {
+       case AHC_EISA:
+       {
+               u_int biosctrl;
+               u_int scsiconf;
+               u_int scsiconf1;
+
+               biosctrl = ahc_inb(ahc, HA_274_BIOSCTRL);
+               scsiconf = ahc_inb(ahc, SCSICONF);
+               scsiconf1 = ahc_inb(ahc, SCSICONF + 1);
+
+               /* Get the primary channel information */
+               if ((biosctrl & CHANNEL_B_PRIMARY) != 0)
+                       ahc->flags |= 1;
+
+               if ((biosctrl & BIOSMODE) == BIOSDISABLED) {
+                       ahc->flags |= AHC_USEDEFAULTS;
+               } else {
+                       if ((ahc->features & AHC_WIDE) != 0) {
+                               ahc->our_id = scsiconf1 & HWSCSIID;
+                               if (scsiconf & TERM_ENB)
+                                       ahc->flags |= AHC_TERM_ENB_A;
+                       } else {
+                               ahc->our_id = scsiconf & HSCSIID;
+                               ahc->our_id_b = scsiconf1 & HSCSIID;
+                               if (scsiconf & TERM_ENB)
+                                       ahc->flags |= AHC_TERM_ENB_A;
+                               if (scsiconf1 & TERM_ENB)
+                                       ahc->flags |= AHC_TERM_ENB_B;
+                       }
+               }
+               if ((ahc_inb(ahc, HA_274_BIOSGLOBAL) & HA_274_EXTENDED_TRANS))
+                       ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B;
+               break;
+       }
+       case AHC_VL:
+       {
+               have_seeprom = aha2840_load_seeprom(ahc);
+               break;
+       }
+       default:
+               break;
+       }
+       if (have_seeprom == 0) {
+               free(ahc->seep_config, M_DEVBUF);
+               ahc->seep_config = NULL;
+       }
+
+       /*
+        * Ensure autoflush is enabled
+        */
+       ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~AUTOFLUSHDIS);
+
+       /* Setup the FIFO threshold and the bus off time */
+       hostconf = ahc_inb(ahc, HOSTCONF);
+       ahc_outb(ahc, BUSSPD, hostconf & DFTHRSH);
+       ahc_outb(ahc, BUSTIME, (hostconf << 2) & BOFF);
+
+       /*
+        * Generic aic7xxx initialization.
+        */
+       error = ahc_init(ahc);
+       if (error != 0)
+               return (error);
+
+       error = aic7770_map_int(ahc, irq);
+       if (error != 0)
+               return (error);
+
+       ahc_list_lock(&l);
+       /*
+        * Link this softc in with all other ahc instances.
+        */
+       ahc_softc_insert(ahc);
+
+       /*
+        * Enable the board's BUS drivers
+        */
+       ahc_outb(ahc, BCTL, ENABLE);
+
+       /*
+        * Allow interrupts.
+        */
+       ahc_lock(ahc, &s);
+       ahc_intr_enable(ahc, TRUE);
+       ahc_unlock(ahc, &s);
+
+       ahc_list_unlock(&l);
+
+       return (0);
+}
+
+/*
+ * Read the 284x SEEPROM.
+ */
+static int
+aha2840_load_seeprom(struct ahc_softc *ahc)
+{
+       struct  seeprom_descriptor sd;
+       struct  seeprom_config *sc;
+       int     have_seeprom;
+       uint8_t scsi_conf;
+
+       sd.sd_ahc = ahc;
+       sd.sd_control_offset = SEECTL_2840;
+       sd.sd_status_offset = STATUS_2840;
+       sd.sd_dataout_offset = STATUS_2840;             
+       sd.sd_chip = C46;
+       sd.sd_MS = 0;
+       sd.sd_RDY = EEPROM_TF;
+       sd.sd_CS = CS_2840;
+       sd.sd_CK = CK_2840;
+       sd.sd_DO = DO_2840;
+       sd.sd_DI = DI_2840;
+       sc = ahc->seep_config;
+
+       if (bootverbose)
+               printf("%s: Reading SEEPROM...", ahc_name(ahc));
+       have_seeprom = ahc_read_seeprom(&sd, (uint16_t *)&sc,
+                                       /*start_addr*/0, sizeof(sc)/2);
+
+       if (have_seeprom) {
+
+               if (ahc_verify_cksum(sc) == 0) {
+                       if(bootverbose)
+                               printf ("checksum error\n");
+                       have_seeprom = 0;
+               } else if (bootverbose) {
+                       printf("done.\n");
+               }
+       }
+
+       if (!have_seeprom) {
+               if (bootverbose)
+                       printf("%s: No SEEPROM available\n", ahc_name(ahc));
+               ahc->flags |= AHC_USEDEFAULTS;
+       } else {
+               /*
+                * Put the data we've collected down into SRAM
+                * where ahc_init will find it.
+                */
+               int      i;
+               int      max_targ;
+               uint16_t discenable;
+
+               max_targ = (ahc->features & AHC_WIDE) != 0 ? 16 : 8;
+               discenable = 0;
+               for (i = 0; i < max_targ; i++){
+                       uint8_t target_settings;
+
+                       target_settings = (sc->device_flags[i] & CFXFER) << 4;
+                       if (sc->device_flags[i] & CFSYNCH)
+                               target_settings |= SOFS;
+                       if (sc->device_flags[i] & CFWIDEB)
+                               target_settings |= WIDEXFER;
+                       if (sc->device_flags[i] & CFDISC)
+                               discenable |= (0x01 << i);
+                       ahc_outb(ahc, TARG_SCSIRATE + i, target_settings);
+               }
+               ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff));
+               ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff));
+
+               ahc->our_id = sc->brtime_id & CFSCSIID;
+
+               scsi_conf = (ahc->our_id & 0x7);
+               if (sc->adapter_control & CFSPARITY)
+                       scsi_conf |= ENSPCHK;
+               if (sc->adapter_control & CFRESETB)
+                       scsi_conf |= RESET_SCSI;
+
+               if (sc->bios_control & CF284XEXTEND)            
+                       ahc->flags |= AHC_EXTENDED_TRANS_A;
+               /* Set SCSICONF info */
+               ahc_outb(ahc, SCSICONF, scsi_conf);
+
+               if (sc->adapter_control & CF284XSTERM)
+                       ahc->flags |= AHC_TERM_ENB_A;
+       }
+       return (have_seeprom);
+}
+
+static int
+ahc_aic7770_VL_setup(struct ahc_softc *ahc)
+{
+       int error;
+
+       error = ahc_aic7770_setup(ahc);
+       ahc->chip |= AHC_VL;
+       return (error);
+}
+
+static int
+ahc_aic7770_EISA_setup(struct ahc_softc *ahc)
+{
+       int error;
+
+       error = ahc_aic7770_setup(ahc);
+       ahc->chip |= AHC_EISA;
+       return (error);
+}
+
+static int
+ahc_aic7770_setup(struct ahc_softc *ahc)
+{
+       ahc->channel = 'A';
+       ahc->channel_b = 'B';
+       ahc->chip = AHC_AIC7770;
+       ahc->features = AHC_AIC7770_FE;
+       ahc->bugs |= AHC_TMODE_WIDEODD_BUG;
+       ahc->flags |= AHC_PAGESCBS;
+       return (0);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic7770_osm.c b/xen/drivers/scsi/aic7xxx/aic7770_osm.c
new file mode 100644 (file)
index 0000000..80d2552
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Linux driver attachment glue for aic7770 based controllers.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7770_osm.c#11 $
+ */
+
+#include "aic7xxx_osm.h"
+
+#define MINSLOT                        1
+#define NUMSLOTS               16
+#define IDOFFSET               0x80
+
+int
+aic7770_linux_probe(Scsi_Host_Template *template)
+{
+#if defined(__i386__) || defined(__alpha__)
+       struct aic7770_identity *entry;
+       struct ahc_softc *ahc;
+       int i, slot;
+       int eisaBase;
+       int found;
+
+       eisaBase = 0x1000 + AHC_EISA_SLOT_OFFSET;
+       found = 0;
+       for (slot = 1; slot < NUMSLOTS; eisaBase+=0x1000, slot++) {
+               uint32_t eisa_id;
+               size_t   id_size;
+
+               if (check_region(eisaBase, AHC_EISA_IOSIZE) != 0)
+                       continue;
+
+               eisa_id = 0;
+               id_size = sizeof(eisa_id);
+               for (i = 0; i < 4; i++) {
+                       /* VLcards require priming*/
+                       outb(0x80 + i, eisaBase + IDOFFSET);
+                       eisa_id |= inb(eisaBase + IDOFFSET + i)
+                                  << ((id_size-i-1) * 8);
+               }
+               if (eisa_id & 0x80000000)
+                       continue;  /* no EISA card in slot */
+
+               entry = aic7770_find_device(eisa_id);
+               if (entry != NULL) {
+                       char     buf[80];
+                       char    *name;
+                       int      error;
+
+                       /*
+                        * Allocate a softc for this card and
+                        * set it up for attachment by our
+                        * common detect routine.
+                        */
+                       sprintf(buf, "ahc_eisa:%d", slot);
+                       name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+                       if (name == NULL)
+                               break;
+                       strcpy(name, buf);
+                       ahc = ahc_alloc(template, name);
+                       if (ahc == NULL) {
+                               /*
+                                * If we can't allocate this one,
+                                * chances are we won't be able to
+                                * allocate future card structures.
+                                */
+                               break;
+                       }
+                       error = aic7770_config(ahc, entry, eisaBase);
+                       if (error != 0) {
+                               ahc->bsh.ioport = 0;
+                               ahc_free(ahc);
+                               continue;
+                       }
+                       found++;
+               }
+       }
+       return (found);
+#else
+       return (0);
+#endif
+}
+
+int
+aic7770_map_registers(struct ahc_softc *ahc, u_int port)
+{
+       /*
+        * Lock out other contenders for our i/o space.
+        */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+       request_region(port, AHC_EISA_IOSIZE, "aic7xxx");
+#else
+       if (request_region(port, AHC_EISA_IOSIZE, "aic7xxx") == 0)
+               return (ENOMEM);
+#endif
+       ahc->tag = BUS_SPACE_PIO;
+       ahc->bsh.ioport = port;
+       return (0);
+}
+
+int
+aic7770_map_int(struct ahc_softc *ahc, u_int irq)
+{
+       int error;
+       int shared;
+
+       shared = 0;
+       if ((ahc->flags & AHC_EDGE_INTERRUPT) == 0)
+               shared = SA_SHIRQ;
+
+       error = request_irq(irq, ahc_linux_isr, shared, "aic7xxx", ahc);
+       if (error == 0)
+               ahc->platform_data->irq = irq;
+       
+       return (-error);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx.h b/xen/drivers/scsi/aic7xxx/aic79xx.h
new file mode 100644 (file)
index 0000000..b4a8abb
--- /dev/null
@@ -0,0 +1,1297 @@
+/*
+ * Core definitions and data structures shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.h#46 $
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AIC79XX_H_
+#define _AIC79XX_H_
+
+/* Register Definitions */
+#include "aic79xx_reg.h"
+
+/************************* Forward Declarations *******************************/
+struct ahd_platform_data;
+struct scb_platform_data;
+
+/****************************** Useful Macros *********************************/
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define NUM_ELEMENTS(array) (sizeof(array) / sizeof(*array))
+
+#define ALL_CHANNELS '\0'
+#define ALL_TARGETS_MASK 0xFFFF
+#define INITIATOR_WILDCARD     (~0)
+#define        SCB_LIST_NULL           0xFF00
+#define        SCB_LIST_NULL_LE        (ahd_htole16(SCB_LIST_NULL))
+#define SCBID_IS_NULL(scbid) (((scbid) & 0xFF00 ) == SCB_LIST_NULL)
+
+#define SCSIID_TARGET(ahd, scsiid)     \
+       (((scsiid) & TID) >> TID_SHIFT)
+#define SCSIID_OUR_ID(scsiid)          \
+       ((scsiid) & OID)
+#define SCSIID_CHANNEL(ahd, scsiid) ('A')
+#define        SCB_IS_SCSIBUS_B(ahd, scb) (0)
+#define        SCB_GET_OUR_ID(scb) \
+       SCSIID_OUR_ID((scb)->hscb->scsiid)
+#define        SCB_GET_TARGET(ahd, scb) \
+       SCSIID_TARGET((ahd), (scb)->hscb->scsiid)
+#define        SCB_GET_CHANNEL(ahd, scb) \
+       SCSIID_CHANNEL(ahd, (scb)->hscb->scsiid)
+#define        SCB_GET_LUN(scb) \
+       ((scb)->hscb->lun)
+#define SCB_GET_TARGET_OFFSET(ahd, scb)        \
+       SCB_GET_TARGET(ahd, scb)
+#define SCB_GET_TARGET_MASK(ahd, scb) \
+       (0x01 << (SCB_GET_TARGET_OFFSET(ahd, scb)))
+/*
+ * TCLs have the following format: TTTTLLLLLLLL
+ */
+#define TCL_TARGET_OFFSET(tcl) \
+       ((((tcl) >> 4) & TID) >> 4)
+#define TCL_LUN(tcl) \
+       (tcl & (AHD_NUM_LUNS_NONPKT - 1))
+#define BUILD_TCL(scsiid, lun) \
+       ((lun) | (((scsiid) & TID) << 4))
+#define BUILD_TCL_RAW(target, channel, lun) \
+       ((lun) | ((target) << 8))
+
+#define SCB_GET_TAG(scb) \
+       ahd_le16toh(scb->hscb->tag)
+
+#ifndef        AHD_TARGET_MODE
+#undef AHD_TMODE_ENABLE
+#define        AHD_TMODE_ENABLE 0
+#endif
+
+/**************************** Driver Constants ********************************/
+/*
+ * The maximum number of supported targets.
+ */
+#define AHD_NUM_TARGETS 16
+
+/*
+ * The maximum number of supported luns.
+ * The identify message only supports 64 luns in non-packetized transfers.
+ * You can have 2^64 luns when information unit transfers are enabled,
+ * but until we see a need to support that many, we support 256.
+ */
+#define AHD_NUM_LUNS_NONPKT 64
+#define AHD_NUM_LUNS 256
+
+/*
+ * The maximum transfer per S/G segment.
+ */
+#define AHD_MAXTRANSFER_SIZE    0x00ffffff     /* limited by 24bit counter */
+
+/*
+ * The maximum amount of SCB storage in hardware on a controller.
+ * This value represents an upper bound.  Due to software design,
+ * we may not be able to use this number.
+ */
+#define AHD_SCB_MAX    512
+
+/*
+ * The maximum number of concurrent transactions supported per driver instance.
+ * Sequencer Control Blocks (SCBs) store per-transaction information.
+ * We are limited to 510 because:
+ *     1) SCB storage space holds us to at most 512.
+ *     2) Our input queue scheme requires one SCB to always be reserved
+ *        in advance of queuing any SCBs.  This takes us down to 511.
+ *     3) To handle our output queue correctly on machines that only
+ *        support 32bit stores, we must clear the array 4 bytes at a
+ *        time.  To avoid colliding with a DMA write from the sequencer,
+ *        we must be sure that 2, 16bit slots are empty when we write to
+ *        clear the queue.  This restricts us to only 511 SCBs: 1 that
+ *        just completed and the known additional empty slot in the queue
+ *        that precedes it.
+#define AHD_MAX_QUEUE  510
+ */
+#define AHD_MAX_QUEUE  255
+
+/*
+ * Define the size of our QIN and QOUT FIFOs.  They must be a power of 2
+ * in size and accomodate as many transactions as can be queued concurrently.
+ */
+#define        AHD_QIN_SIZE    512
+#define        AHD_QOUT_SIZE   512
+
+#define AHD_QIN_WRAP(x) ((x) & (AHD_QIN_SIZE-1))
+#define AHD_QOUT_WRAP(x) ((x) & (AHD_QOUT_SIZE-1))
+
+/*
+ * The maximum amount of SCB storage we allocate in host memory.  This
+ * number should reflect the 1 additional SCB we require to handle our
+ * qinfifo mechanism.
+ */
+#define AHD_SCB_MAX_ALLOC (AHD_MAX_QUEUE+1)
+
+/*
+ * Ring Buffer of incoming target commands.
+ * We allocate 256 to simplify the logic in the sequencer
+ * by using the natural wrap point of an 8bit counter.
+ */
+#define AHD_TMODE_CMDS 256
+
+/* Reset line assertion time in us */
+#define AHD_BUSRESET_DELAY     250
+
+/******************* Chip Characteristics/Operating Settings  *****************/
+/*
+ * Chip Type
+ * The chip order is from least sophisticated to most sophisticated.
+ */
+typedef enum {
+       AHD_NONE        = 0x0000,
+       AHD_CHIPID_MASK = 0x00FF,
+       AHD_AIC7901     = 0x0001,
+       AHD_AIC7902     = 0x0002,
+       AHD_PCI         = 0x0100,       /* Bus type PCI */
+       AHD_PCIX        = 0x0200,       /* Bus type PCIX */
+       AHD_BUS_MASK    = 0x0F00
+} ahd_chip;
+
+/*
+ * Features available in each chip type.
+ */
+typedef enum {
+       AHD_FENONE      = 0x00000,
+       AHD_WIDE        = 0x00001,      /* Wide Channel */
+       AHD_MULTI_FUNC  = 0x00100,      /* Multi-Function Twin Channel Device */
+       AHD_TARGETMODE  = 0x01000,      /* Has tested target mode support */
+       AHD_MULTIROLE   = 0x02000,      /* Space for two roles at a time */
+       AHD_REMOVABLE   = 0x00000,      /* Hot-Swap supported - None so far*/
+       AHD_AIC7901_FE  = AHD_FENONE,
+       AHD_AIC7902_FE  = AHD_MULTI_FUNC
+} ahd_feature;
+
+/*
+ * Bugs in the silicon that we work around in software.
+ */
+typedef enum {
+       AHD_BUGNONE             = 0x0000,
+       AHD_SENT_SCB_UPDATE_BUG = 0x0001,
+       AHD_ABORT_LQI_BUG       = 0x0002,
+       AHD_PKT_BITBUCKET_BUG   = 0x0004,
+       AHD_LONG_SETIMO_BUG     = 0x0008,
+       AHD_NLQICRC_DELAYED_BUG = 0x0010,
+       AHD_SCSIRST_BUG         = 0x0020,
+       AHD_PCIX_ARBITER_BUG    = 0x0040,
+       AHD_PCIX_SPLIT_BUG      = 0x0080,
+       AHD_PCIX_CHIPRST_BUG    = 0x0100,
+       AHD_PCIX_MMAPIO_BUG     = 0x0200,
+       /* Bug workarounds that can be disabled on non-PCIX busses. */
+       AHD_PCIX_BUG_MASK       = AHD_PCIX_ARBITER_BUG
+                               | AHD_PCIX_SPLIT_BUG
+                               | AHD_PCIX_CHIPRST_BUG
+                               | AHD_PCIX_MMAPIO_BUG,
+       AHD_LQO_ATNO_BUG        = 0x0400,
+       AHD_AUTOFLUSH_BUG       = 0x0800,
+       AHD_CLRLQO_AUTOCLR_BUG  = 0x1000,
+       AHD_PKTIZED_STATUS_BUG  = 0x2000
+} ahd_bug;
+
+/*
+ * Configuration specific settings.
+ * The driver determines these settings by probing the
+ * chip/controller's configuration.
+ */
+typedef enum {
+       AHD_FNONE             = 0x00000,
+       AHD_PRIMARY_CHANNEL   = 0x00003,/*
+                                        * The channel that should
+                                        * be probed first.
+                                        */
+       AHD_USEDEFAULTS       = 0x00004,/*
+                                        * For cards without an seeprom
+                                        * or a BIOS to initialize the chip's
+                                        * SRAM, we use the default target
+                                        * settings.
+                                        */
+       AHD_SEQUENCER_DEBUG   = 0x00008,
+       AHD_RESET_BUS_A       = 0x00010,
+       AHD_EXTENDED_TRANS_A  = 0x00020,
+       AHD_TERM_ENB_A        = 0x00040,
+       AHD_SPCHK_ENB_A       = 0x00080,
+       AHD_STPWLEVEL_A       = 0x00100,
+       AHD_INITIATORROLE     = 0x00200,/*
+                                        * Allow initiator operations on
+                                        * this controller.
+                                        */
+       AHD_TARGETROLE        = 0x00400,/*
+                                        * Allow target operations on this
+                                        * controller.
+                                        */
+       AHD_RESOURCE_SHORTAGE = 0x00800,
+       AHD_TQINFIFO_BLOCKED  = 0x01000,/* Blocked waiting for ATIOs */
+       AHD_INT50_SPEEDFLEX   = 0x02000,/*
+                                        * Internal 50pin connector
+                                        * sits behind an aic3860
+                                        */
+       AHD_BIOS_ENABLED      = 0x04000,
+       AHD_ALL_INTERRUPTS    = 0x08000,
+       AHD_39BIT_ADDRESSING  = 0x10000,/* Use 39 bit addressing scheme. */
+       AHD_64BIT_ADDRESSING  = 0x20000,/* Use 64 bit addressing scheme. */
+       AHD_CURRENT_SENSING   = 0x40000,
+       AHD_SCB_CONFIG_USED   = 0x80000,/* No SEEPROM but SCB had info. */
+       AHD_CPQ_BOARD         = 0x100000
+} ahd_flag;
+
+/************************* Hardware  SCB Definition ***************************/
+
+/*
+ * The driver keeps up to MAX_SCB scb structures per card in memory.  The SCB
+ * consists of a "hardware SCB" mirroring the fields availible on the card
+ * and additional information the kernel stores for each transaction.
+ *
+ * To minimize space utilization, a portion of the hardware scb stores
+ * different data during different portions of a SCSI transaction.
+ * As initialized by the host driver for the initiator role, this area
+ * contains the SCSI cdb (or a pointer to the  cdb) to be executed.  After
+ * the cdb has been presented to the target, this area serves to store
+ * residual transfer information and the SCSI status byte.
+ * For the target role, the contents of this area do not change, but
+ * still serve a different purpose than for the initiator role.  See
+ * struct target_data for details.
+ */
+
+/*
+ * Status information embedded in the shared poriton of
+ * an SCB after passing the cdb to the target.  The kernel
+ * driver will only read this data for transactions that
+ * complete abnormally.
+ */
+struct initiator_status {
+       uint32_t residual_datacnt;      /* Residual in the current S/G seg */
+       uint32_t residual_sgptr;        /* The next S/G for this transfer */
+       uint8_t  scsi_status;           /* Standard SCSI status byte */
+};
+
+struct target_status {
+       uint32_t residual_datacnt;      /* Residual in the current S/G seg */
+       uint32_t residual_sgptr;        /* The next S/G for this transfer */
+       uint8_t  scsi_status;           /* SCSI status to give to initiator */
+       uint8_t  target_phases;         /* Bitmap of phases to execute */
+       uint8_t  data_phase;            /* Data-In or Data-Out */
+       uint8_t  initiator_tag;         /* Initiator's transaction tag */
+};
+
+/*
+ * Initiator mode SCB shared data area.
+ * If the embedded CDB is 12 bytes or less, we embed
+ * the sense buffer address in the SCB.  This allows
+ * us to retrieve sense information without interupting
+ * the host in packetized mode.
+ */
+typedef uint32_t sense_addr_t;
+#define MAX_CDB_LEN 16
+#define MAX_CDB_LEN_WITH_SENSE_ADDR (MAX_CDB_LEN - sizeof(sense_addr_t))
+union initiator_data {
+       uint64_t cdbptr;
+       uint8_t  cdb[MAX_CDB_LEN];
+       struct {
+               uint8_t  cdb[MAX_CDB_LEN_WITH_SENSE_ADDR];
+               sense_addr_t sense_addr;
+       } cdb_plus_saddr;
+};
+
+/*
+ * Target mode version of the shared data SCB segment.
+ */
+struct target_data {
+       uint32_t spare[2];      
+       uint8_t  scsi_status;           /* SCSI status to give to initiator */
+       uint8_t  target_phases;         /* Bitmap of phases to execute */
+       uint8_t  data_phase;            /* Data-In or Data-Out */
+       uint8_t  initiator_tag;         /* Initiator's transaction tag */
+};
+
+struct hardware_scb {
+/*0*/  union {
+               union   initiator_data idata;
+               struct  target_data tdata;
+               struct  initiator_status istatus;
+               struct  target_status tstatus;
+       } shared_data;
+/*
+ * A word about residuals.
+ * The scb is presented to the sequencer with the dataptr and datacnt
+ * fields initialized to the contents of the first S/G element to
+ * transfer.  The sgptr field is initialized to the bus address for
+ * the S/G element that follows the first in the in core S/G array
+ * or'ed with the SG_FULL_RESID flag.  Sgptr may point to an invalid
+ * S/G entry for this transfer (single S/G element transfer with the
+ * first elements address and length preloaded in the dataptr/datacnt
+ * fields).  If no transfer is to occur, sgptr is set to SG_LIST_NULL.
+ * The SG_FULL_RESID flag ensures that the residual will be correctly
+ * noted even if no data transfers occur.  Once the data phase is entered,
+ * the residual sgptr and datacnt are loaded from the sgptr and the
+ * datacnt fields.  After each S/G element's dataptr and length are
+ * loaded into the hardware, the residual sgptr is advanced.  After
+ * each S/G element is expired, its datacnt field is checked to see
+ * if the LAST_SEG flag is set.  If so, SG_LIST_NULL is set in the
+ * residual sg ptr and the transfer is considered complete.  If the
+ * sequencer determines that there is a residual in the tranfer, or
+ * there is non-zero status, it will set the SG_STATUS_VALID flag in
+ * sgptr and dma the scb back into host memory.  To sumarize:
+ *
+ * Sequencer:
+ *     o A residual has occurred if SG_FULL_RESID is set in sgptr,
+ *       or residual_sgptr does not have SG_LIST_NULL set.
+ *
+ *     o We are transfering the last segment if residual_datacnt has
+ *       the SG_LAST_SEG flag set.
+ *
+ * Host:
+ *     o A residual can only have occurred if a completed scb has the
+ *       SG_STATUS_VALID flag set.  Inspection of the SCSI status field,
+ *       the residual_datacnt, and the residual_sgptr field will tell
+ *       for sure.
+ *
+ *     o residual_sgptr and sgptr refer to the "next" sg entry
+ *       and so may point beyond the last valid sg entry for the
+ *       transfer.
+ */ 
+#define SG_PTR_MASK    0xFFFFFFF8
+/*16*/ uint8_t  cdb_len;
+/*17*/ uint8_t  task_management;
+/*18*/ uint16_t tag;
+/*20*/ uint32_t next_hscb_busaddr;
+/*24*/ uint64_t dataptr;
+/*32*/ uint32_t datacnt;       /* Byte 3 is spare. */
+/*36*/ uint32_t sgptr;
+/*40*/ uint8_t  control;       /* See SCB_CONTROL in aic79xx.reg for details */
+/*41*/ uint8_t  scsiid;        /*
+                                * Selection out Id
+                                * Our Id (bits 0-3) Their ID (bits 4-7)
+                                */
+/*42*/ uint8_t  lun;
+/*43*/ uint8_t  task_attribute_nonpkt_tag;
+/*44*/ uint32_t hscb_busaddr;
+/******* Fields below are not Downloaded (Sequencer may use for scratch) ******/
+/*48*/  uint8_t         spare[16];
+};
+
+/************************ Kernel SCB Definitions ******************************/
+/*
+ * Some fields of the SCB are OS dependent.  Here we collect the
+ * definitions for elements that all OS platforms need to include
+ * in there SCB definition.
+ */
+
+/*
+ * Definition of a scatter/gather element as transfered to the controller.
+ * The aic7xxx chips only support a 24bit length.  We use the top byte of
+ * the length to store additional address bits and a flag to indicate
+ * that a given segment terminates the transfer.  This gives us an
+ * addressable range of 512GB on machines with 64bit PCI or with chips
+ * that can support dual address cycles on 32bit PCI busses.
+ */
+struct ahd_dma_seg {
+       uint32_t        addr;
+       uint32_t        len;
+#define        AHD_DMA_LAST_SEG        0x80000000
+#define        AHD_SG_HIGH_ADDR_MASK   0x7F000000
+#define        AHD_SG_LEN_MASK         0x00FFFFFF
+};
+
+struct ahd_dma64_seg {
+       uint64_t        addr;
+       uint32_t        len;
+       uint32_t        pad;
+};
+
+struct map_node {
+       bus_dmamap_t             dmamap;
+       bus_addr_t               physaddr;
+       uint8_t                 *vaddr;
+       SLIST_ENTRY(map_node)    links;
+};
+
+/*
+ * The current state of this SCB.
+ */
+typedef enum {
+       SCB_FREE                = 0x0000,
+       SCB_TRANSMISSION_ERROR  = 0x0001,/*
+                                         * We detected a parity or CRC
+                                         * error that has effected the
+                                         * payload of the command.  This
+                                         * flag is checked when normal
+                                         * status is returned to catch
+                                         * the case of a target not
+                                         * responding to our attempt
+                                         * to report the error.
+                                         */
+       SCB_OTHERTCL_TIMEOUT    = 0x0002,/*
+                                         * Another device was active
+                                         * during the first timeout for
+                                         * this SCB so we gave ourselves
+                                         * an additional timeout period
+                                         * in case it was hogging the
+                                         * bus.
+                                         */
+       SCB_DEVICE_RESET        = 0x0004,
+       SCB_SENSE               = 0x0008,
+       SCB_CDB32_PTR           = 0x0010,
+       SCB_RECOVERY_SCB        = 0x0020,
+       SCB_AUTO_NEGOTIATE      = 0x0040,/* Negotiate to achieve goal. */
+       SCB_NEGOTIATE           = 0x0080,/* Negotiation forced for command. */
+       SCB_ABORT               = 0x0100,
+       SCB_UNTAGGEDQ           = 0x0200,
+       SCB_ACTIVE              = 0x0400,
+       SCB_TARGET_IMMEDIATE    = 0x0800,
+       SCB_PACKETIZED          = 0x1000,
+       SCB_EXPECT_PPR_BUSFREE  = 0x2000,
+       SCB_PKT_SENSE           = 0x4000,
+       SCB_CMDPHASE_ABORT      = 0x8000
+} scb_flag;
+
+struct scb {
+       struct  hardware_scb     *hscb;
+       union {
+               SLIST_ENTRY(scb)  sle;
+               TAILQ_ENTRY(scb)  tqe;
+       } links;
+       LIST_ENTRY(scb)           pending_links;
+       ahd_io_ctx_t              io_ctx;
+       struct ahd_softc         *ahd_softc;
+       scb_flag                  flags;
+#ifndef __linux__
+       bus_dmamap_t              dmamap;
+#endif
+       struct scb_platform_data *platform_data;
+       struct map_node          *hscb_map;
+       struct map_node          *sg_map;
+       struct map_node          *sense_map;
+       void                     *sg_list;
+       uint8_t                  *sense_data;
+       bus_addr_t                sg_list_busaddr;
+       bus_addr_t                sense_busaddr;
+       u_int                     sg_count;/* How full ahd_dma_seg is */
+};
+
+struct scb_data {
+       SLIST_HEAD(, scb) free_scbs;    /*
+                                        * Pool of SCBs ready to be assigned
+                                        * commands to execute.
+                                        */
+       struct  scb *scbindex[AHD_SCB_MAX];
+                                       /*
+                                        * Mapping from tag to SCB.
+                                        */
+       /*
+        * "Bus" addresses of our data structures.
+        */
+       bus_dma_tag_t    hscb_dmat;     /* dmat for our hardware SCB array */
+       bus_dma_tag_t    sg_dmat;       /* dmat for our sg segments */
+       bus_dma_tag_t    sense_dmat;    /* dmat for our sense buffers */
+       SLIST_HEAD(, map_node) hscb_maps;
+       SLIST_HEAD(, map_node) sg_maps;
+       SLIST_HEAD(, map_node) sense_maps;
+       int              scbs_left;     /* unallocated scbs in head map_node */
+       int              sgs_left;      /* unallocated sgs in head map_node */
+       int              sense_left;    /* unallocated sense in head map_node */
+       uint16_t         numscbs;
+       uint16_t         maxhscbs;      /* Number of SCBs on the card */
+       uint8_t          init_level;    /*
+                                        * How far we've initialized
+                                        * this structure.
+                                        */
+};
+
+/************************ Target Mode Definitions *****************************/
+
+/*
+ * Connection desciptor for select-in requests in target mode.
+ */
+struct target_cmd {
+       uint8_t scsiid;         /* Our ID and the initiator's ID */
+       uint8_t identify;       /* Identify message */
+       uint8_t bytes[22];      /* 
+                                * Bytes contains any additional message
+                                * bytes terminated by 0xFF.  The remainder
+                                * is the cdb to execute.
+                                */
+       uint8_t cmd_valid;      /*
+                                * When a command is complete, the firmware
+                                * will set cmd_valid to all bits set.
+                                * After the host has seen the command,
+                                * the bits are cleared.  This allows us
+                                * to just peek at host memory to determine
+                                * if more work is complete. cmd_valid is on
+                                * an 8 byte boundary to simplify setting
+                                * it on aic7880 hardware which only has
+                                * limited direct access to the DMA FIFO.
+                                */
+       uint8_t pad[7];
+};
+
+/*
+ * Number of events we can buffer up if we run out
+ * of immediate notify ccbs.
+ */
+#define AHD_TMODE_EVENT_BUFFER_SIZE 8
+struct ahd_tmode_event {
+       uint8_t initiator_id;
+       uint8_t event_type;     /* MSG type or EVENT_TYPE_BUS_RESET */
+#define        EVENT_TYPE_BUS_RESET 0xFF
+       uint8_t event_arg;
+};
+
+/*
+ * Per enabled lun target mode state.
+ * As this state is directly influenced by the host OS'es target mode
+ * environment, we let the OS module define it.  Forward declare the
+ * structure here so we can store arrays of them, etc. in OS neutral
+ * data structures.
+ */
+#ifdef AHD_TARGET_MODE 
+struct ahd_tmode_lstate {
+       struct cam_path *path;
+       struct ccb_hdr_slist accept_tios;
+       struct ccb_hdr_slist immed_notifies;
+       struct ahd_tmode_event event_buffer[AHD_TMODE_EVENT_BUFFER_SIZE];
+       uint8_t event_r_idx;
+       uint8_t event_w_idx;
+};
+#else
+struct ahd_tmode_lstate;
+#endif
+
+/******************** Transfer Negotiation Datastructures *********************/
+#define AHD_TRANS_CUR          0x01    /* Modify current neogtiation status */
+#define AHD_TRANS_ACTIVE       0x03    /* Assume this target is on the bus */
+#define AHD_TRANS_GOAL         0x04    /* Modify negotiation goal */
+#define AHD_TRANS_USER         0x08    /* Modify user negotiation settings */
+#define AHD_PERIOD_ASYNC       0xFF
+#define AHD_PERIOD_10MHz       0x19
+
+/*
+ * Transfer Negotiation Information.
+ */
+struct ahd_transinfo {
+       uint8_t protocol_version;       /* SCSI Revision level */
+       uint8_t transport_version;      /* SPI Revision level */
+       uint8_t width;                  /* Bus width */
+       uint8_t period;                 /* Sync rate factor */
+       uint8_t offset;                 /* Sync offset */
+       uint8_t ppr_options;            /* Parallel Protocol Request options */
+};
+
+/*
+ * Per-initiator current, goal and user transfer negotiation information. */
+struct ahd_initiator_tinfo {
+       struct ahd_transinfo curr;
+       struct ahd_transinfo goal;
+       struct ahd_transinfo user;
+};
+
+/*
+ * Per enabled target ID state.
+ * Pointers to lun target state as well as sync/wide negotiation information
+ * for each initiator<->target mapping.  For the initiator role we pretend
+ * that we are the target and the targets are the initiators since the
+ * negotiation is the same regardless of role.
+ */
+struct ahd_tmode_tstate {
+       struct ahd_tmode_lstate*        enabled_luns[AHD_NUM_LUNS];
+       struct ahd_initiator_tinfo      transinfo[AHD_NUM_TARGETS];
+
+       /*
+        * Per initiator state bitmasks.
+        */
+       uint16_t         auto_negotiate;/* Auto Negotiation Required */
+       uint16_t         discenable;    /* Disconnection allowed  */
+       uint16_t         tagenable;     /* Tagged Queuing allowed */
+};
+
+/*
+ * Points of interest along the negotiated transfer scale.
+ */
+#define AHD_SYNCRATE_MAX       0x8
+#define AHD_SYNCRATE_160       0x8
+#define AHD_SYNCRATE_PACED     0x8
+#define AHD_SYNCRATE_DT                0x9
+#define AHD_SYNCRATE_ULTRA2    0xa
+#define AHD_SYNCRATE_ULTRA     0xc
+#define AHD_SYNCRATE_FAST      0x19
+#define AHD_SYNCRATE_MIN_DT    AHD_SYNCRATE_FAST
+#define AHD_SYNCRATE_SYNC      0x32
+#define AHD_SYNCRATE_MIN       0x60
+#define        AHD_SYNCRATE_ASYNC      0xFF
+
+/*
+ * In RevA, the synctable uses a 120MHz rate for the period
+ * factor 8 and 160MHz for the period factor 7.  The 120MHz
+ * rate never made it into the official SCSI spec, so we must
+ * compensate when setting the negotiation table for Rev A
+ * parts.
+ */
+#define AHD_SYNCRATE_REVA_120  0x8
+#define AHD_SYNCRATE_REVA_160  0x7
+
+/***************************** Lookup Tables **********************************/
+/*
+ * Phase -> name and message out response
+ * to parity errors in each phase table. 
+ */
+struct ahd_phase_table_entry {
+        uint8_t phase;
+        uint8_t mesg_out; /* Message response to parity errors */
+       char *phasemsg;
+};
+
+/************************** Serial EEPROM Format ******************************/
+
+struct seeprom_config {
+/*
+ * Per SCSI ID Configuration Flags
+ */
+       uint16_t device_flags[16];      /* words 0-15 */
+#define                CFXFER          0x003F  /* synchronous transfer rate */
+#define                        CFXFER_ASYNC    0x3F
+#define                CFQAS           0x0040  /* Negotiate QAS */
+#define                CFPACKETIZED    0x0080  /* Negotiate Packetized Transfers */
+#define                CFSTART         0x0100  /* send start unit SCSI command */
+#define                CFINCBIOS       0x0200  /* include in BIOS scan */
+#define                CFDISC          0x0400  /* enable disconnection */
+#define                CFMULTILUNDEV   0x0800  /* Probe multiple luns in BIOS scan */
+#define                CFWIDEB         0x1000  /* wide bus device */
+#define                CFHOSTMANAGED   0x8000  /* Managed by a RAID controller */
+
+/*
+ * BIOS Control Bits
+ */
+       uint16_t bios_control;          /* word 16 */
+#define                CFSUPREM        0x0001  /* support all removeable drives */
+#define                CFSUPREMB       0x0002  /* support removeable boot drives */
+#define                CFBIOSSTATE     0x000C  /* BIOS Action State */
+#define                    CFBS_DISABLED       0x00
+#define                    CFBS_ENABLED        0x04
+#define                    CFBS_DISABLED_SCAN  0x08
+#define                CFENABLEDV      0x0010  /* Perform Domain Validation */
+#define                CFCTRL_A        0x0020  /* BIOS displays Ctrl-A message */      
+#define                CFSPARITY       0x0040  /* SCSI parity */
+#define                CFEXTEND        0x0080  /* extended translation enabled */
+#define                CFBOOTCD        0x0100  /* Support Bootable CD-ROM */
+#define                CFMSG_LEVEL     0x0600  /* BIOS Message Level */
+#define                        CFMSG_VERBOSE   0x0000
+#define                        CFMSG_SILENT    0x0200
+#define                        CFMSG_DIAG      0x0400
+#define                CFRESETB        0x0800  /* reset SCSI bus at boot */
+/*             UNUSED          0xf000  */
+
+/*
+ * Host Adapter Control Bits
+ */
+       uint16_t adapter_control;       /* word 17 */   
+#define                CFAUTOTERM      0x0001  /* Perform Auto termination */
+#define                CFSTERM         0x0002  /* SCSI low byte termination */
+#define                CFWSTERM        0x0004  /* SCSI high byte termination */
+#define                CFSEAUTOTERM    0x0008  /* Ultra2 Perform secondary Auto Term*/
+#define                CFSELOWTERM     0x0010  /* Ultra2 secondary low term */
+#define                CFSEHIGHTERM    0x0020  /* Ultra2 secondary high term */
+#define                CFSTPWLEVEL     0x0040  /* Termination level control */
+#define                CFBIOSAUTOTERM  0x0080  /* Perform Auto termination */
+#define                CFTERM_MENU     0x0100  /* BIOS displays termination menu */    
+#define                CFCLUSTERENB    0x8000  /* Cluster Enable */
+
+/*
+ * Bus Release Time, Host Adapter ID
+ */
+       uint16_t brtime_id;             /* word 18 */
+#define                CFSCSIID        0x000f  /* host adapter SCSI ID */
+/*             UNUSED          0x00f0  */
+#define                CFBRTIME        0xff00  /* bus release time/PCI Latency Time */
+
+/*
+ * Maximum targets
+ */
+       uint16_t max_targets;           /* word 19 */   
+#define                CFMAXTARG       0x00ff  /* maximum targets */
+#define                CFBOOTLUN       0x0f00  /* Lun to boot from */
+#define                CFBOOTID        0xf000  /* Target to boot from */
+       uint16_t res_1[10];             /* words 20-29 */
+       uint16_t signature;             /* BIOS Signature */
+#define                CFSIGNATURE     0x400
+       uint16_t checksum;              /* word 31 */
+};
+
+/****************************** Flexport Logic ********************************/
+#define FLXADDR_TERMCTL                        0x0
+#define                FLX_TERMCTL_ENSECHIGH   0x8
+#define                FLX_TERMCTL_ENSECLOW    0x4
+#define                FLX_TERMCTL_ENPRIHIGH   0x2
+#define                FLX_TERMCTL_ENPRILOW    0x1
+#define FLXADDR_ROMSTAT_CURSENSECTL    0x1
+#define                FLX_ROMSTAT_SEECFG      0xF0
+#define                FLX_ROMSTAT_EECFG       0x0F
+#define                FLX_ROMSTAT_SEE_93C66   0x00
+#define                FLX_ROMSTAT_SEE_NONE    0xF0
+#define                FLX_ROMSTAT_EE_512x8    0x0
+#define                FLX_ROMSTAT_EE_1MBx8    0x1
+#define                FLX_ROMSTAT_EE_2MBx8    0x2
+#define                FLX_ROMSTAT_EE_4MBx8    0x3
+#define                FLX_ROMSTAT_EE_16MBx8   0x4
+#define                CURSENSE_ENB    0x1
+#define        FLXADDR_FLEXSTAT                0x2
+#define                FLX_FSTAT_BUSY          0x1
+#define FLXADDR_CURRENT_STAT           0x4
+#define                FLX_CSTAT_SEC_HIGH      0xC0
+#define                FLX_CSTAT_SEC_LOW       0x30
+#define                FLX_CSTAT_PRI_HIGH      0x0C
+#define                FLX_CSTAT_PRI_LOW       0x03
+#define                FLX_CSTAT_MASK          0x03
+#define                FLX_CSTAT_SHIFT         2
+#define                FLX_CSTAT_OKAY          0x0
+#define                FLX_CSTAT_OVER          0x1
+#define                FLX_CSTAT_UNDER         0x2
+#define                FLX_CSTAT_INVALID       0x3
+
+int            ahd_read_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+                                u_int start_addr, u_int count);
+
+int            ahd_write_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+                                 u_int start_addr, u_int count);
+int            ahd_wait_seeprom(struct ahd_softc *ahd);
+int            ahd_verify_cksum(struct seeprom_config *sc);
+int            ahd_acquire_seeprom(struct ahd_softc *ahd);
+void           ahd_release_seeprom(struct ahd_softc *ahd);
+
+/****************************  Message Buffer *********************************/
+typedef enum {
+       MSG_FLAG_NONE                   = 0x00,
+       MSG_FLAG_EXPECT_PPR_BUSFREE     = 0x01,
+       MSG_FLAG_IU_REQ_CHANGED         = 0x02,
+       MSG_FLAG_EXPECT_IDE_BUSFREE     = 0x04,
+       MSG_FLAG_PACKETIZED             = 0x08
+} ahd_msg_flags;
+
+typedef enum {
+       MSG_TYPE_NONE                   = 0x00,
+       MSG_TYPE_INITIATOR_MSGOUT       = 0x01,
+       MSG_TYPE_INITIATOR_MSGIN        = 0x02,
+       MSG_TYPE_TARGET_MSGOUT          = 0x03,
+       MSG_TYPE_TARGET_MSGIN           = 0x04
+} ahd_msg_type;
+
+typedef enum {
+       MSGLOOP_IN_PROG,
+       MSGLOOP_MSGCOMPLETE,
+       MSGLOOP_TERMINATED
+} msg_loop_stat;
+
+/*********************** Software Configuration Structure *********************/
+TAILQ_HEAD(scb_tailq, scb);
+
+struct ahd_suspend_channel_state {
+       uint8_t scsiseq;
+       uint8_t sxfrctl0;
+       uint8_t sxfrctl1;
+       uint8_t simode0;
+       uint8_t simode1;
+       uint8_t seltimer;
+       uint8_t seqctl;
+};
+
+struct ahd_suspend_state {
+       struct  ahd_suspend_channel_state channel[2];
+       uint8_t optionmode;
+       uint8_t dscommand0;
+       uint8_t dspcistatus;
+       /* hsmailbox */
+       uint8_t crccontrol1;
+       uint8_t scbbaddr;
+       /* Host and sequencer SCB counts */
+       uint8_t dff_thrsh;
+       uint8_t *scratch_ram;
+       uint8_t *btt;
+};
+
+typedef void (*ahd_bus_intr_t)(struct ahd_softc *);
+
+typedef enum {
+       AHD_MODE_DFF0,
+       AHD_MODE_DFF1,
+       AHD_MODE_CCHAN,
+       AHD_MODE_SCSI,
+       AHD_MODE_CFG,
+       AHD_MODE_UNKNOWN
+} ahd_mode;
+
+#define AHD_MK_MSK(x) (0x01 << (x))
+#define AHD_MODE_DFF0_MSK      AHD_MK_MSK(AHD_MODE_DFF0)
+#define AHD_MODE_DFF1_MSK      AHD_MK_MSK(AHD_MODE_DFF1)
+#define AHD_MODE_CCHAN_MSK     AHD_MK_MSK(AHD_MODE_CCHAN)
+#define AHD_MODE_SCSI_MSK      AHD_MK_MSK(AHD_MODE_SCSI)
+#define AHD_MODE_CFG_MSK       AHD_MK_MSK(AHD_MODE_CFG)
+#define AHD_MODE_UNKNOWN_MSK   AHD_MK_MSK(AHD_MODE_UNKNOWN)
+#define AHD_MODE_ANY_MSK (~0)
+
+typedef uint8_t ahd_mode_state;
+
+typedef void ahd_callback_t (void *);
+
+struct ahd_softc {
+       bus_space_tag_t           tags[2];
+       bus_space_handle_t        bshs[2];
+#ifndef __linux__
+       bus_dma_tag_t             buffer_dmat;   /* dmat for buffer I/O */
+#endif
+       struct scb_data           scb_data;
+
+       struct scb               *next_queued_scb;
+
+       /*
+        * SCBs that have been sent to the controller
+        */
+       LIST_HEAD(, scb)          pending_scbs;
+
+       /*
+        * Current register window mode information.
+        */
+       ahd_mode                  dst_mode;
+       ahd_mode                  src_mode;
+
+       /*
+        * Saved register window mode information
+        * used for restore on next unpause.
+        */
+       ahd_mode                  saved_dst_mode;
+       ahd_mode                  saved_src_mode;
+
+       /*
+        * Counting lock for deferring the release of additional
+        * untagged transactions from the untagged_queues.  When
+        * the lock is decremented to 0, all queues in the
+        * untagged_queues array are run.
+        */
+       u_int                     untagged_queue_lock;
+
+       /*
+        * Per-target queue of untagged-transactions.  The
+        * transaction at the head of the queue is the
+        * currently pending untagged transaction for the
+        * target.  The driver only allows a single untagged
+        * transaction per target.
+        */
+       struct scb_tailq          untagged_queues[AHD_NUM_TARGETS];
+
+       /*
+        * Platform specific data.
+        */
+       struct ahd_platform_data *platform_data;
+
+       /*
+        * Platform specific device information.
+        */
+       ahd_dev_softc_t           dev_softc;
+
+       /*
+        * Bus specific device information.
+        */
+       ahd_bus_intr_t            bus_intr;
+
+       /*
+        * Target mode related state kept on a per enabled lun basis.
+        * Targets that are not enabled will have null entries.
+        * As an initiator, we keep one target entry for our initiator
+        * ID to store our sync/wide transfer settings.
+        */
+       struct ahd_tmode_tstate  *enabled_targets[AHD_NUM_TARGETS];
+
+       /*
+        * The black hole device responsible for handling requests for
+        * disabled luns on enabled targets.
+        */
+       struct ahd_tmode_lstate  *black_hole;
+
+       /*
+        * Device instance currently on the bus awaiting a continue TIO
+        * for a command that was not given the disconnect priveledge.
+        */
+       struct ahd_tmode_lstate  *pending_device;
+
+       /*
+        * Timer handles for timer driven callbacks.
+        */
+       ahd_timer_t               reset_timer;
+
+       /*
+        * Card characteristics
+        */
+       ahd_chip                  chip;
+       ahd_feature               features;
+       ahd_bug                   bugs;
+       ahd_flag                  flags;
+       struct seeprom_config    *seep_config;
+
+       /* Values to store in the SEQCTL register for pause and unpause */
+       uint8_t                   unpause;
+       uint8_t                   pause;
+
+       /* Command Queues */
+       uint16_t                  qoutfifonext;
+       uint16_t                  qinfifonext;
+       uint16_t                  qinfifo[AHD_SCB_MAX];
+       uint16_t                 *qoutfifo;
+
+       /* Critical Section Data */
+       struct cs                *critical_sections;
+       u_int                     num_critical_sections;
+
+       /* Buffer for handling packetized bitbucket. */
+       uint8_t                  *overrun_buf;
+
+       /* Links for chaining softcs */
+       TAILQ_ENTRY(ahd_softc)    links;
+
+       /* Channel Names ('A', 'B', etc.) */
+       char                      channel;
+
+       /* Initiator Bus ID */
+       uint8_t                   our_id;
+
+       /*
+        * PCI error detection.
+        */
+       int                       unsolicited_ints;
+
+       /*
+        * Target incoming command FIFO.
+        */
+       struct target_cmd        *targetcmds;
+       uint8_t                   tqinfifonext;
+
+       /*
+        * Incoming and outgoing message handling.
+        */
+       uint8_t                   send_msg_perror;
+       ahd_msg_flags             msg_flags;
+       ahd_msg_type              msg_type;
+       uint8_t                   msgout_buf[12];/* Message we are sending */
+       uint8_t                   msgin_buf[12];/* Message we are receiving */
+       u_int                     msgout_len;   /* Length of message to send */
+       u_int                     msgout_index; /* Current index in msgout */
+       u_int                     msgin_index;  /* Current index in msgin */
+
+       /*
+        * Mapping information for data structures shared
+        * between the sequencer and kernel.
+        */
+       bus_dma_tag_t             parent_dmat;
+       bus_dma_tag_t             shared_data_dmat;
+       bus_dmamap_t              shared_data_dmamap;
+       bus_addr_t                shared_data_busaddr;
+
+       /* Information saved through suspend/resume cycles */
+       struct ahd_suspend_state  suspend_state;
+
+       /* Number of enabled target mode device on this card */
+       u_int                     enabled_luns;
+
+       /* Initialization level of this data structure */
+       u_int                     init_level;
+
+       /* PCI cacheline size. */
+       u_int                     pci_cachesize;
+
+       /* Per-Unit descriptive information */
+       const char               *description;
+       const char               *bus_description;
+       char                     *name;
+       int                       unit;
+
+       /* Selection Timer settings */
+       int                       seltime;
+
+       uint16_t                  user_discenable;/* Disconnection allowed  */
+       uint16_t                  user_tagenable;/* Tagged Queuing allowed */
+};
+
+TAILQ_HEAD(ahd_softc_tailq, ahd_softc);
+extern struct ahd_softc_tailq ahd_tailq;
+
+/************************ Active Device Information ***************************/
+typedef enum {
+       ROLE_UNKNOWN,
+       ROLE_INITIATOR,
+       ROLE_TARGET
+} role_t;
+
+struct ahd_devinfo {
+       int      our_scsiid;
+       int      target_offset;
+       uint16_t target_mask;
+       u_int    target;
+       u_int    lun;
+       char     channel;
+       role_t   role;          /*
+                                * Only guaranteed to be correct if not
+                                * in the busfree state.
+                                */
+};
+
+/****************************** PCI Structures ********************************/
+#define AHD_PCI_IOADDR0        PCIR_MAPS       /* I/O BAR*/
+#define AHD_PCI_MEMADDR        (PCIR_MAPS + 4) /* Memory BAR */
+#define AHD_PCI_IOADDR1        (PCIR_MAPS + 12)/* Second I/O BAR */
+
+typedef int (ahd_device_setup_t)(struct ahd_softc *);
+
+struct ahd_pci_identity {
+       uint64_t                 full_id;
+       uint64_t                 id_mask;
+       char                    *name;
+       ahd_device_setup_t      *setup;
+};
+extern struct ahd_pci_identity ahd_pci_ident_table [];
+extern const u_int ahd_num_pci_devs;
+
+/***************************** VL/EISA Declarations ***************************/
+struct aic7770_identity {
+       uint32_t                 full_id;
+       uint32_t                 id_mask;
+       char                    *name;
+       ahd_device_setup_t      *setup;
+};
+extern struct aic7770_identity aic7770_ident_table [];
+extern const int ahd_num_aic7770_devs;
+
+#define AHD_EISA_SLOT_OFFSET   0xc00
+#define AHD_EISA_IOSIZE                0x100
+
+/*************************** Function Declarations ****************************/
+/******************************************************************************/
+u_int                  ahd_find_busy_tcl(struct ahd_softc *ahd, u_int tcl);
+void                   ahd_set_disconnected_list(struct ahd_softc *ahd,
+                                                 u_int target, u_int lun,
+                                                 u_int scbid);
+void                   ahd_busy_tcl(struct ahd_softc *ahd,
+                                    u_int tcl, u_int busyid);
+static __inline void   ahd_unbusy_tcl(struct ahd_softc *ahd, u_int tcl);
+static __inline void
+ahd_unbusy_tcl(struct ahd_softc *ahd, u_int tcl)
+{
+       ahd_busy_tcl(ahd, tcl, SCB_LIST_NULL);
+}
+
+/***************************** PCI Front End *********************************/
+struct ahd_pci_identity        *ahd_find_pci_device(ahd_dev_softc_t);
+int                     ahd_pci_config(struct ahd_softc *,
+                                       struct ahd_pci_identity *);
+
+/*************************** EISA/VL Front End ********************************/
+struct aic7770_identity *aic7770_find_device(uint32_t);
+int                     aic7770_config(struct ahd_softc *ahd,
+                                       struct aic7770_identity *);
+
+/************************** SCB and SCB queue management **********************/
+int            ahd_probe_scbs(struct ahd_softc *);
+void           ahd_run_untagged_queues(struct ahd_softc *ahd);
+void           ahd_run_untagged_queue(struct ahd_softc *ahd,
+                                      struct scb_tailq *queue);
+void           ahd_qinfifo_requeue_tail(struct ahd_softc *ahd,
+                                        struct scb *scb);
+int            ahd_match_scb(struct ahd_softc *ahd, struct scb *scb,
+                             int target, char channel, int lun,
+                             u_int tag, role_t role);
+
+/****************************** Initialization ********************************/
+struct ahd_softc       *ahd_alloc(void *platform_arg, char *name);
+int                     ahd_softc_init(struct ahd_softc *);
+void                    ahd_controller_info(struct ahd_softc *ahd, char *buf);
+int                     ahd_init(struct ahd_softc *ahd);
+int                     ahd_default_config(struct ahd_softc *ahd);
+int                     ahd_parse_cfgdata(struct ahd_softc *ahd,
+                                          struct seeprom_config *sc);
+void                    ahd_intr_enable(struct ahd_softc *ahd, int enable);
+void                    ahd_pause_and_flushwork(struct ahd_softc *ahd);
+int                     ahd_suspend(struct ahd_softc *ahd); 
+int                     ahd_resume(struct ahd_softc *ahd);
+void                    ahd_softc_insert(struct ahd_softc *);
+struct ahd_softc       *ahd_find_softc(struct ahd_softc *ahd);
+void                    ahd_set_unit(struct ahd_softc *, int);
+void                    ahd_set_name(struct ahd_softc *, char *);
+void                    ahd_alloc_scbs(struct ahd_softc *ahd);
+void                    ahd_free(struct ahd_softc *ahd);
+int                     ahd_reset(struct ahd_softc *ahd);
+void                    ahd_shutdown(void *arg);
+int                    ahd_write_flexport(struct ahd_softc *ahd,
+                                          u_int addr, u_int value);
+int                    ahd_read_flexport(struct ahd_softc *ahd, u_int addr,
+                                         uint8_t *value);
+int                    ahd_wait_flexport(struct ahd_softc *ahd);
+
+/*************************** Interrupt Services *******************************/
+void                   ahd_pci_intr(struct ahd_softc *ahd);
+void                   ahd_clear_intstat(struct ahd_softc *ahd);
+void                   ahd_run_qoutfifo(struct ahd_softc *ahd);
+#ifdef AHD_TARGET_MODE
+void                   ahd_run_tqinfifo(struct ahd_softc *ahd, int paused);
+#endif
+void                   ahd_handle_hwerrint(struct ahd_softc *ahd);
+void                   ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat);
+void                   ahd_handle_scsiint(struct ahd_softc *ahd,
+                                          u_int intstat);
+void                   ahd_clear_critical_section(struct ahd_softc *ahd);
+
+/***************************** Error Recovery *********************************/
+typedef enum {
+       SEARCH_COMPLETE,
+       SEARCH_COUNT,
+       SEARCH_REMOVE,
+       SEARCH_PRINT
+} ahd_search_action;
+int                    ahd_search_qinfifo(struct ahd_softc *ahd, int target,
+                                          char channel, int lun, u_int tag,
+                                          role_t role, uint32_t status,
+                                          ahd_search_action action);
+int                    ahd_search_disc_list(struct ahd_softc *ahd, int target,
+                                            char channel, int lun, u_int tag,
+                                            int stop_on_first, int remove,
+                                            int save_state);
+void                   ahd_freeze_devq(struct ahd_softc *ahd, struct scb *scb);
+int                    ahd_reset_channel(struct ahd_softc *ahd, char channel,
+                                         int initiate_reset);
+int                    ahd_abort_scbs(struct ahd_softc *ahd, int target,
+                                      char channel, int lun, u_int tag,
+                                      role_t role, uint32_t status);
+void                   ahd_restart(struct ahd_softc *ahd);
+void                   ahd_clear_fifo(struct ahd_softc *ahd, u_int fifo);
+void                   ahd_handle_scb_status(struct ahd_softc *ahd,
+                                             struct scb *scb);
+void                   ahd_handle_scsi_status(struct ahd_softc *ahd,
+                                              struct scb *scb);
+void                   ahd_calc_residual(struct ahd_softc *ahd,
+                                         struct scb *scb);
+/*************************** Utility Functions ********************************/
+struct ahd_phase_table_entry*
+                       ahd_lookup_phase_entry(int phase);
+void                   ahd_compile_devinfo(struct ahd_devinfo *devinfo,
+                                           u_int our_id, u_int target,
+                                           u_int lun, char channel,
+                                           role_t role);
+/************************** Transfer Negotiation ******************************/
+void                   ahd_find_syncrate(struct ahd_softc *ahd, u_int *period,
+                                         u_int *ppr_options, u_int maxsync);
+void                   ahd_validate_offset(struct ahd_softc *ahd,
+                                           struct ahd_initiator_tinfo *tinfo,
+                                           u_int period, u_int *offset,
+                                           int wide, role_t role);
+void                   ahd_validate_width(struct ahd_softc *ahd,
+                                          struct ahd_initiator_tinfo *tinfo,
+                                          u_int *bus_width,
+                                          role_t role);
+int                    ahd_update_neg_request(struct ahd_softc*,
+                                              struct ahd_devinfo*,
+                                              struct ahd_tmode_tstate*,
+                                              struct ahd_initiator_tinfo*,
+                                              int /*force*/);
+void                   ahd_set_width(struct ahd_softc *ahd,
+                                     struct ahd_devinfo *devinfo,
+                                     u_int width, u_int type, int paused);
+void                   ahd_set_syncrate(struct ahd_softc *ahd,
+                                        struct ahd_devinfo *devinfo,
+                                        u_int period, u_int offset,
+                                        u_int ppr_options,
+                                        u_int type, int paused);
+typedef enum {
+       AHD_QUEUE_NONE,
+       AHD_QUEUE_BASIC,
+       AHD_QUEUE_TAGGED
+} ahd_queue_alg;
+
+void                   ahd_set_tags(struct ahd_softc *ahd,
+                                    struct ahd_devinfo *devinfo,
+                                    ahd_queue_alg alg);
+
+/**************************** Target Mode *************************************/
+#ifdef AHD_TARGET_MODE
+void           ahd_send_lstate_events(struct ahd_softc *,
+                                      struct ahd_tmode_lstate *);
+void           ahd_handle_en_lun(struct ahd_softc *ahd,
+                                 struct cam_sim *sim, union ccb *ccb);
+cam_status     ahd_find_tmode_devs(struct ahd_softc *ahd,
+                                   struct cam_sim *sim, union ccb *ccb,
+                                   struct ahd_tmode_tstate **tstate,
+                                   struct ahd_tmode_lstate **lstate,
+                                   int notfound_failure);
+#ifndef AHD_TMODE_ENABLE
+#define AHD_TMODE_ENABLE 0
+#endif
+#endif
+/******************************* Debug ***************************************/
+#ifdef AHD_DEBUG
+extern uint32_t ahd_debug;
+#define AHD_SHOW_MISC          0x001
+#define AHD_SHOW_SENSE         0x002
+#define AHD_DUMP_SEEPROM       0x004
+#define AHD_SHOW_TERMCTL       0x008
+#define AHD_SHOW_MEMORY                0x010
+#define AHD_SHOW_MESSAGES      0x020
+#define AHD_SHOW_MODEPTR       0x040
+#define AHD_SHOW_SELTO         0x080
+#define AHD_SHOW_FIFOS         0x100
+#define AHD_SHOW_QFULL         0x200
+#define AHD_SHOW_QUEUE         0x400
+#define AHD_SHOW_TQIN          0x800
+#endif
+void                   ahd_print_scb(struct scb *scb);
+void                   ahd_dump_sglist(struct scb *scb);
+void                   ahd_dump_all_cards_state(void);
+void                   ahd_dump_card_state(struct ahd_softc *ahd);
+void                   ahd_dump_scbs(struct ahd_softc *ahd);
+#endif /* _AIC79XX_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx.reg b/xen/drivers/scsi/aic7xxx/aic79xx.reg
new file mode 100644 (file)
index 0000000..b681bd3
--- /dev/null
@@ -0,0 +1,3716 @@
+/*
+ * Aic79xx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#34 $"
+
+/*
+ * This file is processed by the aic7xxx_asm utility for use in assembling
+ * firmware for the aic79xx family of SCSI host adapters as well as to generate
+ * a C header file for use in the kernel portion of the Aic79xx driver.
+ */
+
+/* Register window Modes */
+#define M_DFF0         0
+#define M_DFF1         1
+#define M_CCHAN                2
+#define M_SCSI         3
+#define M_CFG          4
+#define M_DST_SHIFT    4
+
+#define MK_MODE(src, dst) ((src) | ((dst) << M_DST_SHIFT))
+#define SET_MODE(src, dst)                                     \
+       SET_SRC_MODE    src;                                    \
+       SET_DST_MODE    dst;                                    \
+       mvi     MK_MODE(src, dst) call set_mode_work_around
+
+/*
+ * Mode Pointer
+ * Controls which of the 5, 512byte, address spaces should be used
+ * as the source and destination of any register accesses in our
+ * register window.
+ */
+register MODE_PTR {
+       address                 0x000
+       access_mode     RW
+       mask    DST_MODE        0x70
+       mask    SRC_MODE        0x07
+       mode_pointer
+}
+
+const SRC_MODE_SHIFT   0
+const DST_MODE_SHIFT   4
+
+/*
+ * Host Interrupt Status
+ */
+register INTSTAT {
+       address                 0x001
+       access_mode     RW
+       bit     HWERRINT        0x80
+       bit     BRKADRINT       0x40
+       bit     SWTMINT         0x20
+       bit     PCIINT          0x10
+       bit     SCSIINT         0x08
+       bit     SEQINT          0x04
+       bit     CMDCMPLT        0x02
+       bit     SPLTINT         0x01
+       mask    INT_PEND 0xFF
+}
+
+/*
+ * Sequencer Interrupt Code
+ */
+register SEQINTCODE {
+       address                 0x002
+       access_mode     RW
+       mask    BAD_PHASE       1               /* unknown scsi bus phase */
+       mask    SEND_REJECT     2               /* sending a message reject */
+       mask    PROTO_VIOLATION 3               /* Protocol Violation */
+       mask    NO_MATCH        4               /* no cmd match for reconnect */
+       mask    IGN_WIDE_RES    5               /* Complex IGN Wide Res Msg */
+       mask    PDATA_REINIT    6               /*
+                                                * Returned to data phase
+                                                * that requires data
+                                                * transfer pointers to be
+                                                * recalculated from the
+                                                * transfer residual.
+                                                */
+       mask    HOST_MSG_LOOP   7               /*
+                                                * The bus is ready for the
+                                                * host to perform another
+                                                * message transaction.  This
+                                                * mechanism is used for things
+                                                * like sync/wide negotiation
+                                                * that require a kernel based
+                                                * message state engine.
+                                                */
+       mask    BAD_STATUS      8               /* Bad status from target */
+       mask    DATA_OVERRUN    9               /*
+                                                * Target attempted to write
+                                                * beyond the bounds of its
+                                                * command.
+                                                */
+       mask    MKMSG_FAILED    10              /*
+                                                * Target completed command
+                                                * without honoring our ATN
+                                                * request to issue a message. 
+                                                */
+       mask    MISSED_BUSFREE  11              /*
+                                                * The sequencer never saw
+                                                * the bus go free after
+                                                * either a command complete
+                                                * or disconnect message.
+                                                */
+       mask    SCB_MISMATCH    12              /*
+                                                * Downloaded SCB's tag does
+                                                * not match the entry we
+                                                * intended to download.
+                                                */
+       mask    NO_FREE_SCB     13              /*
+                                                * get_free_or_disc_scb failed.
+                                                */
+       mask    OUT_OF_RANGE    14
+       mask    NO_FREE_FIFO    15
+       mask    DUMP_CARD_STATE 16
+       mask    ILLEGAL_PHASE   17
+       mask    INVALID_SEQINT  18
+       mask    CFG4ISTAT_INTR  19
+       mask    STATUS_OVERRUN  20
+       mask    CFG4OVERRUN     21
+       mask    SNAPSHOTCLRCHN  22
+       mask    MONITORDRAIN    23
+       mask    ENTERING_NONPACK 24
+       mask    PCIX_ARBITOR_WW 25
+}
+
+/*
+ * Clear Host Interrupt
+ */
+register CLRINT {
+       address                 0x003
+       access_mode     WO
+       bit     CLRBRKADRINT    0x40
+       bit     CLRSWTMINT      0x20
+       bit     CLRSCSIINT      0x08
+       bit     CLRSEQINT       0x04
+       bit     CLRCMDINT       0x02
+       bit     CLRSPLTINT      0x01
+}
+
+/*
+ * Error Register
+ */
+register ERROR {
+       address                 0x004
+       access_mode     RO
+       bit     CIOPARERR       0x80
+       bit     MPARERR         0x20
+       bit     DPARERR         0x10
+       bit     SQPARERR        0x08
+       bit     ILLOPCODE       0x04
+       bit     DSCTMOUT        0x02
+}
+
+/*
+ * Clear Error
+ */
+register CLRERR {
+       address                 0x004
+       access_mode     WO
+       bit     CLRCIOPARERR    0x80
+       bit     CLRMPARERR      0x20
+       bit     CLRDPARERR      0x10
+       bit     CLRSQPARERR     0x08
+       bit     CLRILLOPCODE    0x04
+       bit     CLRDSCTMOUT     0x02
+}
+
+/*
+ * Host Control Register
+ * Overall host control of the device.
+ */
+register HCNTRL {
+       address                 0x005
+       access_mode     RW
+       bit     POWRDN          0x40
+       bit     SWINT           0x10
+       bit     HCNTRL3         0x08
+       bit     PAUSE           0x04
+       bit     INTEN           0x02
+       bit     CHIPRST         0x01
+       bit     CHIPRSTACK      0x01
+}
+
+/*
+ * Host New SCB Queue Offset
+ */
+register HNSCB_QOFF {
+       address                 0x006
+       access_mode     RW
+       size            2
+}
+
+/*
+ * Host Empty SCB Queue Offset
+ */
+register HESCB_QOFF {
+       address                 0x008
+       access_mode     RW
+}
+
+/*
+ * Host Mailbox
+ */
+register HS_MAILBOX {
+       address                 0x0B
+       access_mode     RW
+       mask    HOST_TQINPOS    0x80    /* Boundary at either 0 or 128 */
+}
+
+/*
+ * Sequencer Interupt Status
+ */
+register SEQINTSTAT {
+       address                 0x0C
+       access_mode     RO
+       bit     SEQ_SWTMRTO     0x10
+       bit     SEQ_SEQINT      0x08
+       bit     SEQ_SCSIINT     0x04
+       bit     SEQ_PCIINT      0x02
+       bit     SEQ_SPLTINT     0x01
+}
+
+/*
+ * Clear SEQ Interrupt
+ */
+register CLRSEQINTSTAT {
+       address                 0x0C0
+       access_mode     WO
+       bit     CLRSEQ_SWTMRTO  0x10
+       bit     CLRSEQ_SEQINT   0x08
+       bit     CLRSEQ_SCSIINT  0x04
+       bit     CLRSEQ_PCIINT   0x02
+       bit     CLRSEQ_SPLTINT  0x01
+}
+
+/*
+ * Software Timer
+ */
+register SWTIMER {
+       address                 0x0E0
+       access_mode     RW
+       size            2
+}
+
+/*
+ * SEQ New SCB Queue Offset
+ */
+register SNSCB_QOFF {
+       address                 0x010
+       access_mode     RW
+       size            2
+       modes           M_CCHAN
+}
+
+/*
+ * SEQ Empty SCB Queue Offset
+ */
+register SESCB_QOFF {
+       address                 0x012
+       access_mode     RW
+       modes           M_CCHAN
+}
+
+/*
+ * SEQ Done SCB Queue Offset
+ */
+register SDSCB_QOFF {
+       address                 0x014
+       access_mode     RW
+       modes           M_CCHAN
+       size            2
+}
+
+/*
+ * Queue Offset Control & Status
+ */
+register QOFF_CTLSTA {
+       address                 0x016
+       access_mode     RW
+       modes           M_CCHAN
+       bit     EMPTY_SCB_AVAIL 0x80
+       bit     NEW_SCB_AVAIL   0x40
+       bit     SDSCB_ROLLOVR   0x20
+       bit     HS_MAILBOX_ACT  0x10
+       mask    SCB_QSIZE       0x0F
+       mask    SCB_QSIZE_4     0x00
+       mask    SCB_QSIZE_8     0x01
+       mask    SCB_QSIZE_16    0x02
+       mask    SCB_QSIZE_32    0x03
+       mask    SCB_QSIZE_64    0x04
+       mask    SCB_QSIZE_128   0x05
+       mask    SCB_QSIZE_256   0x06
+       mask    SCB_QSIZE_512   0x07
+       mask    SCB_QSIZE_1024  0x08
+       mask    SCB_QSIZE_2048  0x09
+       mask    SCB_QSIZE_4096  0x0A
+       mask    SCB_QSIZE_8192  0x0B
+       mask    SCB_QSIZE_16384 0x0C
+}
+
+/*
+ * Interrupt Control
+ */
+register INTCTL {
+       address                 0x018
+       access_mode     RW
+       bit     SWTMINTMASK     0x80
+       bit     SWTMINTEN       0x40
+       bit     SWTIMER_START   0x20
+       bit     AUTOCLRCMDINT   0x10
+       bit     PCIINTEN        0x08
+       bit     SCSIINTEN       0x04
+       bit     SEQINTEN        0x02
+       bit     SPLTINTEN       0x01
+}
+
+/*
+ * Data FIFO Control
+ */
+register DFCNTRL {
+       address                 0x019
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+       bit     PRELOADEN       0x80
+       bit     SCSIEN          0x20
+       bit     SCSIENACK       0x20
+       bit     HDMAEN          0x08
+       bit     HDMAENACK       0x08
+       bit     DIRECTION       0x04
+       bit     DIRECTIONACK    0x04
+       bit     FIFOFLUSH       0x02
+       bit     FIFOFLUSHACK    0x02
+       bit     DIRECTIONEN     0x01
+}
+
+/*
+ * Device Space Command 0
+ */
+register DSCOMMAND0 {
+       address                 0x019
+       access_mode     RW
+       modes           M_CFG
+       bit     CACHETHEN       0x80    /* Cache Threshold enable */
+       bit     DPARCKEN        0x40    /* Data Parity Check Enable */
+       bit     MPARCKEN        0x20    /* Memory Parity Check Enable */
+       bit     EXTREQLCK       0x10    /* External Request Lock */
+       bit     CIOPARCKEN      0x01    /* Internal bus parity error enable */
+}
+
+/*
+ * Data FIFO Status
+ */
+register DFSTATUS {
+       address                 0x01A
+       access_mode     RO
+       modes           M_DFF0, M_DFF1
+       bit     PRELOAD_AVAIL           0x80
+       bit     PKT_PRELOAD_AVAIL       0x40
+       bit     MREQPEND                0x10
+       bit     HDONE                   0x08
+       bit     DFTHRESH                0x04
+       bit     FIFOFULL                0x02
+       bit     FIFOEMP                 0x01
+}
+
+/*
+ * S/G Cache Pointer
+ */
+register SG_CACHE_PRE {
+       address                 0x01B
+       access_mode     WO
+       modes           M_DFF0, M_DFF1
+       mask    SG_ADDR_MASK    0xf8
+       bit     ODD_SEG         0x04
+       bit     LAST_SEG        0x02
+}
+
+register SG_CACHE_SHADOW {
+       address                 0x01B
+       access_mode     RO
+       modes           M_DFF0, M_DFF1
+       mask    SG_ADDR_MASK    0xf8
+       bit     ODD_SEG         0x04
+       bit     LAST_SEG        0x02
+       bit     LAST_SEG_DONE   0x01
+}
+
+/*
+ * Arbiter Control
+ */
+register ARBCTL {
+       address                 0x01B
+       access_mode     RW
+       modes           M_CFG
+       bit     RESET_HARB      0x80
+       bit     RETRY_SWEN      0x08
+       mask    USE_TIME        0x07
+}
+
+/*
+ * Data Channel Host Address
+ */
+register HADDR {
+       address                 0x070
+       access_mode     RW
+       size            8
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * Host Overlay DMA Address
+ */
+register HODMAADR {
+       address                 0x070
+       access_mode     RW
+       size            8
+       modes           M_SCSI
+}
+
+/*
+ * Data Channel Host Count
+ */
+register HCNT {
+       address                 0x078
+       access_mode     RW
+       size            3
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * Host Overlay DMA Count
+ */
+register HODMACNT {
+       address                 0x078
+       access_mode     RW
+       size            2
+       modes           M_SCSI
+}
+
+/*
+ * Host Overlay DMA Enable
+ */
+register HODMAEN {
+       address                 0x07A
+       access_mode     RW
+       modes           M_SCSI
+}
+
+/*
+ * Scatter/Gather Host Address
+ */
+register SGHADDR {
+       address                 0x07C
+       access_mode     RW
+       size            8
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * SCB Host Address
+ */
+register SCBHADDR {
+       address                 0x07C
+       access_mode     RW
+       size            8
+       modes           M_CCHAN
+}
+
+/*
+ * Scatter/Gather Host Count
+ */
+register SGHCNT {
+       address                 0x084
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * SCB Host Count
+ */
+register SCBHCNT {
+       address                 0x084
+       access_mode     RW
+       modes           M_CCHAN
+}
+
+/*
+ * Data FIFO Threshold
+ */
+register DFF_THRSH {
+       address                 0x088
+       access_mode     RW
+       modes           M_CFG
+       mask    WR_DFTHRSH      0x70
+       mask    RD_DFTHRSH      0x07
+       mask    RD_DFTHRSH_MIN  0x00
+       mask    RD_DFTHRSH_25   0x01
+       mask    RD_DFTHRSH_50   0x02
+       mask    RD_DFTHRSH_63   0x03
+       mask    RD_DFTHRSH_75   0x04
+       mask    RD_DFTHRSH_85   0x05
+       mask    RD_DFTHRSH_90   0x06
+       mask    RD_DFTHRSH_MAX  0x07
+       mask    WR_DFTHRSH_MIN  0x00
+       mask    WR_DFTHRSH_25   0x10
+       mask    WR_DFTHRSH_50   0x20
+       mask    WR_DFTHRSH_63   0x30
+       mask    WR_DFTHRSH_75   0x40
+       mask    WR_DFTHRSH_85   0x50
+       mask    WR_DFTHRSH_90   0x60
+       mask    WR_DFTHRSH_MAX  0x70
+}
+
+/*
+ * ROM Address
+ */
+register ROMADDR {
+       address                 0x08A
+       access_mode     RW
+       size            3
+}
+
+/*
+ * ROM Control
+ */
+register ROMCNTRL {
+       address                 0x08D
+       access_mode     RW
+       mask    ROMOP           0xE0
+       mask    ROMSPD          0x18
+       bit     REPEAT          0x02
+       bit     RDY             0x01
+}
+
+/*
+ * ROM Data
+ */
+register ROMDATA {
+       address                 0x08E
+       access_mode     RW
+}
+
+/*
+ * Data Channel Receive Message 0
+ */
+register DCHRXMSG0 {
+       address                 0x090
+       access_mode     RO
+       modes           M_DFF0, M_DFF1
+       mask            CDNUM   0xF8
+       mask            CFNUM   0x07
+}
+
+/*
+ * CMC Recieve Message 0
+ */
+register CMCRXMSG0 {
+       address                 0x090
+       access_mode     RO
+       modes           M_CCHAN
+       mask            CDNUM   0xF8
+       mask            CFNUM   0x07
+}
+
+/*
+ * Overlay Recieve Message 0
+ */
+register OVLYRXMSG0 {
+       address                 0x090
+       access_mode     RO
+       modes           M_SCSI
+       mask            CDNUM   0xF8
+       mask            CFNUM   0x07
+}
+
+/*
+ * Relaxed Order Enable
+ */
+register ROENABLE {
+       address                 0x090
+       access_mode     RW
+       modes           M_CFG
+       bit     MSIROEN         0x20
+       bit     OVLYROEN        0x10
+       bit     CMCROEN         0x08
+       bit     SGROEN          0x04
+       bit     DCH1ROEN        0x02
+       bit     DCH0ROEN        0x01
+}
+
+/*
+ * Data Channel Receive Message 1
+ */
+register DCHRXMSG1 {
+       address                 0x091
+       access_mode     RO
+       modes           M_DFF0, M_DFF1
+       mask    CBNUM           0xFF
+}
+
+/*
+ * CMC Recieve Message 1
+ */
+register CMCRXMSG1 {
+       address                 0x091
+       access_mode     RO
+       modes           M_CCHAN
+       mask    CBNUM           0xFF
+}
+
+/*
+ * Overlay Recieve Message 1
+ */
+register OVLYRXMSG1 {
+       address                 0x091
+       access_mode     RO
+       modes           M_SCSI
+       mask    CBNUM           0xFF
+}
+
+/*
+ * No Snoop Enable
+ */
+register NSENABLE {
+       address                 0x091
+       access_mode     RW
+       modes           M_CFG
+       bit     MSINSEN         0x20
+       bit     OVLYNSEN        0x10
+       bit     CMCNSEN         0x08
+       bit     SGNSEN          0x04
+       bit     DCH1NSEN        0x02
+       bit     DCH0NSEN        0x01
+}
+
+/*
+ * Data Channel Receive Message 2
+ */
+register DCHRXMSG2 {
+       address                 0x092
+       access_mode     RO
+       modes           M_DFF0, M_DFF1
+       mask    MINDEX          0xFF
+}
+
+/*
+ * CMC Recieve Message 2
+ */
+register CMCRXMSG2 {
+       address                 0x092
+       access_mode     RO
+       modes           M_CCHAN
+       mask    MINDEX          0xFF
+}
+
+/*
+ * Overlay Recieve Message 2
+ */
+register OVLYRXMSG2 {
+       address                 0x092
+       access_mode     RO
+       modes           M_SCSI
+       mask    MINDEX          0xFF
+}
+
+/*
+ * Outstanding Split Transactions
+ */
+register OST {
+       address                 0x092
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Data Channel Receive Message 3
+ */
+register DCHRXMSG3 {
+       address                 0x093
+       access_mode     RO
+       modes           M_DFF0, M_DFF1
+       mask    MCLASS          0x0F
+}
+
+/*
+ * CMC Recieve Message 3
+ */
+register CMCRXMSG3 {
+       address                 0x093
+       access_mode     RO
+       modes           M_CCHAN
+       mask    MCLASS          0x0F
+}
+
+/*
+ * Overlay Recieve Message 3
+ */
+register OVLYRXMSG3 {
+       address                 0x093
+       access_mode     RO
+       modes           M_SCSI
+       mask    MCLASS          0x0F
+}
+
+/*
+ * PCI-X Control
+ */
+register PCIXCTL {
+       address                 0x093
+       access_mode     RW
+       modes           M_CFG
+       bit     SERRPULSE       0x80
+       bit     UNEXPSCIEN      0x20
+       bit     SPLTSMADIS      0x10
+       bit     SPLTSTADIS      0x08
+       bit     SRSPDPEEN       0x04
+       bit     TSCSERREN       0x02
+       bit     CMPABCDIS       0x01
+}
+
+/*
+ * CMC Sequencer Byte Count
+ */
+register CMCSEQBCNT {
+       address                 0x094
+       access_mode     RO
+       modes           M_CCHAN
+}
+
+/*
+ * Overlay Sequencer Byte Count
+ */
+register OVLYSEQBCNT {
+       address                 0x094
+       access_mode     RO
+       modes           M_SCSI
+}
+
+/*
+ * Data Channel Sequencer Byte Count
+ */
+register DCHSEQBCNT {
+       address                 0x094
+       access_mode     RO
+       size            2
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * Data Channel Split Status 0
+ */
+register DCHSPLTSTAT0 {
+       address                 0x096
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+       bit     STAETERM        0x80
+       bit     SCBCERR         0x40
+       bit     SCADERR         0x20
+       bit     SCDATBUCKET     0x10
+       bit     CNTNOTCMPLT     0x08
+       bit     RXOVRUN         0x04
+       bit     RXSCEMSG        0x02
+       bit     RXSPLTRSP       0x01
+}
+
+/*
+ * CMC Split Status 0
+ */
+register CMCSPLTSTAT0 {
+       address                 0x096
+       access_mode     RW
+       modes           M_CCHAN
+       bit     STAETERM        0x80
+       bit     SCBCERR         0x40
+       bit     SCADERR         0x20
+       bit     SCDATBUCKET     0x10
+       bit     CNTNOTCMPLT     0x08
+       bit     RXOVRUN         0x04
+       bit     RXSCEMSG        0x02
+       bit     RXSPLTRSP       0x01
+}
+
+/*
+ * Overlay Split Status 0
+ */
+register OVLYSPLTSTAT0 {
+       address                 0x096
+       access_mode     RW
+       modes           M_SCSI
+       bit     STAETERM        0x80
+       bit     SCBCERR         0x40
+       bit     SCADERR         0x20
+       bit     SCDATBUCKET     0x10
+       bit     CNTNOTCMPLT     0x08
+       bit     RXOVRUN         0x04
+       bit     RXSCEMSG        0x02
+       bit     RXSPLTRSP       0x01
+}
+
+/*
+ * Data Channel Split Status 1
+ */
+register DCHSPLTSTAT1 {
+       address                 0x097
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+       bit     RXDATABUCKET    0x01
+}
+
+/*
+ * CMC Split Status 1
+ */
+register CMCSPLTSTAT1 {
+       address                 0x097
+       access_mode     RW
+       modes           M_CCHAN
+       bit     RXDATABUCKET    0x01
+}
+
+/*
+ * Overlay Split Status 1
+ */
+register OVLYSPLTSTAT1 {
+       address                 0x097
+       access_mode     RW
+       modes           M_SCSI
+       bit     RXDATABUCKET    0x01
+}
+
+/*
+ * S/G Receive Message 0
+ */
+register SGRXMSG0 {
+       address                 0x098
+       access_mode     RO
+       modes           M_DFF0, M_DFF1
+       mask            CDNUM   0xF8
+       mask            CFNUM   0x07
+}
+
+/*
+ * S/G Receive Message 1
+ */
+register SGRXMSG1 {
+       address                 0x099
+       access_mode     RO
+       modes           M_DFF0, M_DFF1
+       mask    CBNUM           0xFF
+}
+
+/*
+ * S/G Receive Message 2
+ */
+register SGRXMSG2 {
+       address                 0x09A
+       access_mode     RO
+       modes           M_DFF0, M_DFF1
+       mask    MINDEX          0xFF
+}
+
+/*
+ * S/G Receive Message 3
+ */
+register SGRXMSG3 {
+       address                 0x09B
+       access_mode     RO
+       modes           M_DFF0, M_DFF1
+       mask    MCLASS          0x0F
+}
+
+/*
+ * Slave Split Out Address 0
+ */
+register SLVSPLTOUTADR0 {
+       address                 0x098
+       access_mode     RO
+       modes           M_SCSI
+       mask    LOWER_ADDR      0x7F
+}
+
+/*
+ * Slave Split Out Address 1
+ */
+register SLVSPLTOUTADR1 {
+       address                 0x099
+       access_mode     RO
+       modes           M_SCSI
+       mask    REQ_DNUM        0xF8
+       mask    REQ_FNUM        0x07
+}
+
+/*
+ * Slave Split Out Address 2
+ */
+register SLVSPLTOUTADR2 {
+       address                 0x09A
+       access_mode     RO
+       modes           M_SCSI
+       mask    REQ_BNUM        0xFF
+}
+
+/*
+ * Slave Split Out Address 3
+ */
+register SLVSPLTOUTADR3 {
+       address                 0x09B
+       access_mode     RO
+       modes           M_SCSI
+       bit     RLXORD          020
+       mask    TAG_NUM         0x1F
+}
+
+/*
+ * SG Sequencer Byte Count
+ */
+register SGSEQBCNT {
+       address                 0x09C
+       access_mode     RO
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * Slave Split Out Attribute 0
+ */
+register SLVSPLTOUTATTR0 {
+       address                 0x09C
+       access_mode     RO
+       modes           M_SCSI
+       mask    LOWER_BCNT      0xFF
+}
+
+/*
+ * Slave Split Out Attribute 1
+ */
+register SLVSPLTOUTATTR1 {
+       address                 0x09D
+       access_mode     RO
+       modes           M_SCSI
+       mask    CMPLT_DNUM      0xF8
+       mask    CMPLT_FNUM      0x07
+}
+
+/*
+ * Slave Split Out Attribute 2
+ */
+register SLVSPLTOUTATTR2 {
+       address                 0x09E
+       access_mode     RO
+       size            2
+       modes           M_SCSI
+       mask    CMPLT_BNUM      0xFF
+}
+/*
+ * S/G Split Status 0
+ */
+register SGSPLTSTAT0 {
+       address                 0x09E
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+       bit     STAETERM        0x80
+       bit     SCBCERR         0x40
+       bit     SCADERR         0x20
+       bit     SCDATBUCKET     0x10
+       bit     CNTNOTCMPLT     0x08
+       bit     RXOVRUN         0x04
+       bit     RXSCEMSG        0x02
+       bit     RXSPLTRSP       0x01
+}
+
+/*
+ * S/G Split Status 1
+ */
+register SGSPLTSTAT1 {
+       address                 0x09F
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+       bit     RXDATABUCKET    0x01
+}
+
+/*
+ * Special Function
+ */
+register SFUNCT {
+       address                 0x09f
+       access_mode     RW
+       modes           M_CFG
+       mask    TEST_GROUP      0xF0
+       mask    TEST_NUM        0x0F
+}
+
+/*
+ * Data FIFO 0 PCI Status 
+ */
+register DF0PCISTAT {
+       address                 0x0A0
+       access_mode     RW
+       modes           M_CFG
+       bit     DPE             0x80
+       bit     SSE             0x40
+       bit     RMA             0x20
+       bit     RTA             0x10
+       bit     SCAAPERR        0x08
+       bit     RDPERR          0x04
+       bit     TWATERR         0x02
+       bit     DPR             0x01
+}
+
+/*
+ * Data FIFO 1 PCI Status 
+ */
+register DF1PCISTAT {
+       address                 0x0A1
+       access_mode     RW
+       modes           M_CFG
+       bit     DPE             0x80
+       bit     SSE             0x40
+       bit     RMA             0x20
+       bit     RTA             0x10
+       bit     SCAAPERR        0x08
+       bit     RDPERR          0x04
+       bit     TWATERR         0x02
+       bit     DPR             0x01
+}
+
+/*
+ * S/G PCI Status 
+ */
+register SGPCISTAT {
+       address                 0x0A2
+       access_mode     RW
+       modes           M_CFG
+       bit     DPE             0x80
+       bit     SSE             0x40
+       bit     RMA             0x20
+       bit     RTA             0x10
+       bit     SCAAPERR        0x08
+       bit     RDPERR          0x04
+       bit     DPR             0x01
+}
+
+/*
+ * CMC PCI Status 
+ */
+register CMCPCISTAT {
+       address                 0x0A3
+       access_mode     RW
+       modes           M_CFG
+       bit     DPE             0x80
+       bit     SSE             0x40
+       bit     RMA             0x20
+       bit     RTA             0x10
+       bit     SCAAPERR        0x08
+       bit     RDPERR          0x04
+       bit     TWATERR         0x02
+       bit     DPR             0x01
+}
+
+/*
+ * Overlay PCI Status 
+ */
+register OVLYPCISTAT {
+       address                 0x0A4
+       access_mode     RW
+       modes           M_CFG
+       bit     DPE             0x80
+       bit     SSE             0x40
+       bit     RMA             0x20
+       bit     RTA             0x10
+       bit     SCAAPERR        0x08
+       bit     RDPERR          0x04
+       bit     DPR             0x01
+}
+
+/*
+ * PCI Status for MSI Master DMA Transfer
+ */
+register MSIPCISTAT {
+       address                 0x0A6
+       access_mode     RW
+       modes           M_CFG
+       bit     SSE             0x40
+       bit     RMA             0x20
+       bit     RTA             0x10
+       bit     CLRPENDMSI      0x08
+       bit     TWATERR         0x02
+       bit     DPR             0x01
+}
+
+/*
+ * PCI Status for Target
+ */
+register TARGPCISTAT {
+       address                 0x0A6
+       access_mode     RW
+       modes           M_CFG
+       bit     DPE             0x80
+       bit     SSE             0x40
+       bit     STA             0x08
+       bit     TWATERR         0x02
+}
+
+/*
+ * LQ Packet In
+ * The last LQ Packet recieved
+ */
+register LQIN {
+       address                 0x020
+       access_mode     RW
+       size            20
+       modes           M_DFF0, M_DFF1, M_SCSI
+}
+
+/*
+ * SCB Type Pointer
+ * SCB offset for Target Mode SCB type information
+ */
+register TYPEPTR {
+       address                 0x020
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Queue Tag Pointer
+ * SCB offset to the Two Byte tag identifier used for target mode.
+ */
+register TAGPTR {
+       address                 0x021
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Logical Unit Number Pointer
+ * SCB offset to the LSB (little endian) of the lun field.
+ */
+register LUNPTR {
+       address                 0x022
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Data Length Pointer
+ * SCB offset for the 4 byte data length field in target mode.
+ */
+register DATALENPTR {
+       address                 0x023
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Status Length Pointer
+ * SCB offset to the two byte status field in target SCBs.
+ */
+register STATLENPTR {
+       address                 0x024
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Command Length Pointer
+ * Scb offset for the CDB length field in initiator SCBs.
+ */
+register CMDLENPTR {
+       address                 0x025
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Task Attribute Pointer
+ * Scb offset for the byte field specifying the attribute byte
+ * to be used in command packets.
+ */ 
+register ATTRPTR {
+       address                 0x026
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Task Management Flags Pointer
+ * Scb offset for the byte field specifying the attribute flags
+ * byte to be used in command packets.
+ */ 
+register FLAGPTR {
+       address                 0x027
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Command Pointer
+ * Scb offset for the first byte in the CDB for initiator SCBs.
+ */
+register CMDPTR {
+       address                 0x028
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Queue Next Pointer
+ * Scb offset for the 2 byte "next scb link".
+ */
+register QNEXTPTR {
+       address                 0x029
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * SCSI ID Pointer
+ * Scb offset to the value to place in the SCSIID register
+ * during target mode connections.
+ */
+register IDPTR {
+       address                 0x02A
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Command Aborted Byte Pointer
+ * Offset to the SCB flags field that includes the
+ * "SCB aborted" status bit.
+ */
+register ABRTBYTEPTR {
+       address                 0x02B
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Command Aborted Bit Pointer
+ * Bit offset in the SCB flags field for "SCB aborted" status.
+ */
+register ABRTBITPTR {
+       address                 0x02C
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Logical Unit Number Length
+ * The length, in bytes, of the SCB lun field.
+ */
+register LUNLEN {
+       address                 0x030
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * CDB Limit
+ * The size, in bytes, of the embedded CDB field in initator SCBs.
+ */
+register CDBLIMIT {
+       address                 0x031
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Maximum Commands
+ * The maximum number of commands to issue during a
+ * single packetized connection.
+ */
+register MAXCMD {
+       address                 0x032
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * Maximum Command Counter
+ * The number of commands already sent during this connection
+ */
+register MAXCMDCNT {
+       address                 0x033
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * LQ Packet Reserved Bytes
+ * The bytes to be sent in the currently reserved fileds
+ * of all LQ packets.
+ */
+register LQRSVD01 {
+       address                 0x034
+       access_mode     RW
+       modes           M_SCSI
+}
+register LQRSVD16 {
+       address                 0x035
+       access_mode     RW
+       modes           M_SCSI
+}
+register LQRSVD17 {
+       address                 0x036
+       access_mode     RW
+       modes           M_SCSI
+}
+
+/*
+ * Command Reserved 0
+ * The byte to be sent for the reserved byte 0 of
+ * outgoing command packets.
+ */
+register CMDRSVD0 {
+       address                 0x037
+       access_mode     RW
+       modes           M_CFG
+}
+
+/*
+ * LQ Manager Control 0
+ */
+register LQCTL0 {
+       address                 0x038
+       access_mode     RW
+       modes           M_CFG
+       mask    LQITARGCLT      0xC0
+       mask    LQIINITGCLT     0x30
+       mask    LQ0TARGCLT      0x0C
+       mask    LQ0INITGCLT     0x03
+}
+
+/*
+ * LQ Manager Control 1
+ */
+register LQCTL1 {
+       address                 0x038
+       access_mode     RW
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     PCI2PCI         0x04
+       bit     SINGLECMD       0x02
+       bit     ABORTPENDING    0x01
+}
+
+/*
+ * LQ Manager Control 2
+ */
+register LQCTL2 {
+       address                 0x039
+       access_mode     RW
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     LQIRETRY        0x80
+       bit     LQICONTINUE     0x40
+       bit     LQITOIDLE       0x20
+       bit     LQIPAUSE        0x10
+       bit     LQORETRY        0x08
+       bit     LQOCONTINUE     0x04
+       bit     LQOTOIDLE       0x02
+       bit     LQOPAUSE        0x01
+}
+
+/*
+ * SCSI RAM BIST0
+ */
+register SCSBIST0 {
+       address                 0x039
+       access_mode     RW
+       modes           M_CFG
+       bit     GSBISTERR       0x40
+       bit     GSBISTDONE      0x20
+       bit     GSBISTRUN       0x10
+       bit     OSBISTERR       0x04
+       bit     OSBISTDONE      0x02
+       bit     OSBISTRUN       0x01
+}
+
+/*
+ * SCSI Sequence Control0
+ */
+register SCSISEQ0 {
+       address                 0x03A
+       access_mode     RW
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     TEMODEO         0x80
+       bit     ENSELO          0x40
+       bit     ENARBO          0x20
+       bit     FORCEBUSFREE    0x10
+       bit     SCSIRSTO        0x01
+}
+
+/*
+ * SCSI RAM BIST 1
+ */
+register SCSBIST1 {
+       address                 0x03A
+       access_mode     RW
+       modes           M_CFG
+       bit     NTBISTERR       0x04
+       bit     NTBISTDONE      0x02
+       bit     NTBISTRUN       0x01
+}
+
+/*
+ * SCSI Sequence Control 1
+ */
+register SCSISEQ1 {
+       address                 0x03B
+       access_mode     RW
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     MANUALCTL       0x40
+       bit     ENSELI          0x20
+       bit     ENRSELI         0x10
+       mask    MANUALP         0x0C
+       bit     ENAUTOATNP      0x02
+       bit     ALTSTIM         0x01
+}
+
+/*
+ * SCSI Transfer Control 0
+ */
+register SXFRCTL0 {
+       address                 0x03C
+       access_mode     RW
+       modes           M_SCSI
+       bit     DFON            0x80
+       bit     DFPEXP          0x40
+       bit     BIOSCANCELEN    0x10
+       bit     SPIOEN          0x08
+}
+
+/*
+ * SCSI Transfer Control 1
+ */
+register SXFRCTL1 {
+       address                 0x03D
+       access_mode     RW
+       modes           M_SCSI
+       bit     BITBUCKET       0x80
+       bit     ENSACHK         0x40
+       bit     ENSPCHK         0x20
+       mask    STIMESEL        0x18
+       bit     ENSTIMER        0x04
+       bit     ACTNEGEN        0x02
+       bit     STPWEN          0x01
+}
+
+/*
+ * SCSI Transfer Control 2
+ */
+register SXFRCTL2 {
+       address                 0x03E
+       access_mode     RW
+       modes           M_SCSI
+       bit     AUTORSTDIS      0x10
+       bit     CMDDMAEN        0x08
+       mask    ASU             0x07
+}
+
+/*
+ * SCSI Bus Initiator IDs
+ * Bitmask of observed initiators on the bus.
+ */
+register BUSINITID {
+       address                 0x03C
+       access_mode     RW
+       modes           M_CFG
+       size            2
+}
+
+/*
+ * Data Length Counters
+ * Packet byte counter.
+ */
+register DLCOUNT {
+       address                 0x03C
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+       size            3
+}
+
+/*
+ * Data FIFO Status
+ */
+register DFFSTAT {
+       address                 0x03F
+       access_mode     RW
+       modes           M_SCSI
+       bit     FIFO1FREE       0x20
+       bit     FIFO0FREE       0x10
+       bit     CURRFIFO        0x01
+}
+
+/*
+ * SCSI Bus Target IDs
+ * Bitmask of observed targets on the bus.
+ */
+register BUSTARGID {
+       address                 0x03E
+       access_mode     RW
+       modes           M_CFG
+       size            2
+}
+
+/*
+ * SCSI Control Signal Out
+ */
+register SCSISIGO {
+       address                 0x040
+       access_mode     RW
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     CDO             0x80
+       bit     IOO             0x40
+       bit     MSGO            0x20
+       bit     ATNO            0x10
+       bit     SELO            0x08
+       bit     BSYO            0x04
+       bit     REQO            0x02
+       bit     ACKO            0x01
+/*
+ * Possible phases to write into SCSISIG0
+ */
+       mask    PHASE_MASK      CDO|IOO|MSGO
+       mask    P_DATAOUT       0x00
+       mask    P_DATAIN        IOO
+       mask    P_DATAOUT_DT    P_DATAOUT|MSGO
+       mask    P_DATAIN_DT     P_DATAIN|MSGO
+       mask    P_COMMAND       CDO
+       mask    P_MESGOUT       CDO|MSGO
+       mask    P_STATUS        CDO|IOO
+       mask    P_MESGIN        CDO|IOO|MSGO
+}
+
+register SCSISIGI {
+       address                 0x041
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     CDI             0x80
+       bit     IOI             0x40
+       bit     MSGI            0x20
+       bit     ATNI            0x10
+       bit     SELI            0x08
+       bit     BSYI            0x04
+       bit     REQI            0x02
+       bit     ACKI            0x01
+/*
+ * Possible phases in SCSISIGI
+ */
+       mask    PHASE_MASK      CDI|IOI|MSGI
+       mask    P_DATAOUT       0x00
+       mask    P_DATAIN        IOI
+       mask    P_DATAOUT_DT    P_DATAOUT|MSGI
+       mask    P_DATAIN_DT     P_DATAIN|MSGI
+       mask    P_COMMAND       CDI
+       mask    P_MESGOUT       CDI|MSGI
+       mask    P_STATUS        CDI|IOI
+       mask    P_MESGIN        CDI|IOI|MSGI
+}
+
+/*
+ * Multiple Target IDs
+ * Bitmask of ids to respond as a target.
+ */
+register MULTARGID {
+       address                 0x040
+       access_mode     RW
+       modes           M_CFG
+       size            2
+}
+
+/*
+ * SCSI Phase
+ */
+register SCSIPHASE {
+       address                 0x042
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     STATUS_PHASE    0x20
+       bit     COMMAND_PHASE   0x10
+       bit     MSG_IN_PHASE    0x08
+       bit     MSG_OUT_PHASE   0x04
+       bit     DATA_IN_PHASE   0x02
+       bit     DATA_OUT_PHASE  0x01
+       mask    DATA_PHASE_MASK 0x03
+}
+
+/*
+ * SCSI Data 0 Image
+ */
+register SCSIDAT0_IMG {
+       address                 0x043
+       access_mode     RW
+       modes           M_DFF0, M_DFF1, M_SCSI
+}
+
+/*
+ * SCSI Latched Data
+ */
+register SCSIDAT {
+       address                 0x044
+       access_mode     RW
+       modes           M_DFF0, M_DFF1, M_SCSI
+       size            2
+}
+
+/*
+ * SCSI Data Bus
+ */
+register SCSIBUS {
+       address                 0x046
+       access_mode     RW
+       modes           M_DFF0, M_DFF1, M_SCSI
+       size            2
+}
+
+/*
+ * Target ID In
+ */
+register TARGIDIN {
+       address                 0x048
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     CLKOUT          0x80
+       mask    TARGID          0x0F
+}
+
+/*
+ * Selection/Reselection ID
+ * Upper four bits are the device id.  The ONEBIT is set when the re/selecting
+ * device did not set its own ID.
+ */
+register SELID {
+       address                 0x049
+       access_mode     RW
+       modes           M_DFF0, M_DFF1, M_SCSI
+       mask    SELID_MASK      0xf0
+       bit     ONEBIT          0x08
+}
+
+/*
+ * SCSI Block Control
+ * Controls Bus type and channel selection.  SELWIDE allows for the
+ * coexistence of 8bit and 16bit devices on a wide bus.
+ */
+register SBLKCTL {
+       address                 0x04A
+       access_mode     RW
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     DIAGLEDEN       0x80
+       bit     DIAGLEDON       0x40
+       bit     ENAB40          0x08    /* LVD transceiver active */
+       bit     ENAB20          0x04    /* SE/HVD transceiver active */
+       bit     SELWIDE         0x02
+}
+
+/*
+ * Option Mode
+ */
+register OPTIONMODE {
+       address                 0x04A
+       access_mode     RW
+       modes           M_CFG
+       bit     BIOSCANCTL              0x80
+       bit     AUTOACKEN               0x40
+       bit     BIASCANCTL              0x20
+       bit     BUSFREEREV              0x10
+       bit     ENDGFORMCHK             0x04
+       bit     AUTO_MSGOUT_DE          0x02
+       mask    OPTIONMODE_DEFAULTS     AUTO_MSGOUT_DE
+}
+
+/*
+ * SCSI Status 0
+ */
+register SSTAT0        {
+       address                 0x04B
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     TARGET          0x80    /* Board acting as target */
+       bit     SELDO           0x40    /* Selection Done */
+       bit     SELDI           0x20    /* Board has been selected */
+       bit     SELINGO         0x10    /* Selection In Progress */
+       bit     IOERR           0x08    /* LVD Tranceiver mode changed */
+       bit     OVERRUN         0x04    /* SCSI Offset overrun detected */
+       bit     SPIORDY         0x02    /* SCSI PIO Ready */
+       bit     ARBDO           0x01    /* Arbitration Done Out */
+}
+
+/*
+ * Clear SCSI Interrupt 0
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
+ */
+register CLRSINT0 {
+       address                 0x04B
+       access_mode     WO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     CLRSELDO        0x40
+       bit     CLRSELDI        0x20
+       bit     CLRSELINGO      0x10
+       bit     CLRIOERR        0x08
+       bit     CLROVERRUN      0x04
+       bit     CLRSPIORDY      0x02
+       bit     CLRARBDO        0x01
+}
+
+/*
+ * SCSI Interrupt Mode 0
+ * Setting any bit will enable the corresponding function
+ * in SIMODE0 to interrupt via the IRQ pin.
+ */
+register SIMODE0 {
+       address                 0x04B
+       access_mode     RW
+       modes           M_CFG
+       bit     ENSELDO         0x40
+       bit     ENSELDI         0x20
+       bit     ENSELINGO       0x10
+       bit     ENIOERR         0x08
+       bit     ENOVERRUN       0x04
+       bit     ENSPIORDY       0x02
+       bit     ENARBDO         0x01
+}
+
+/*
+ * SCSI Status 1
+ */
+register SSTAT1 {
+       address                 0x04C
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     SELTO           0x80
+       bit     ATNTARG         0x40
+       bit     SCSIRSTI        0x20
+       bit     PHASEMIS        0x10
+       bit     BUSFREE         0x08
+       bit     SCSIPERR        0x04
+       bit     STRB2FAST       0x02
+       bit     REQINIT         0x01
+}
+
+/*
+ * Clear SCSI Interrupt 1
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+register CLRSINT1 {
+       address                 0x04c
+       access_mode     WO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     CLRSELTIMEO     0x80
+       bit     CLRATNO         0x40
+       bit     CLRSCSIRSTI     0x20
+       bit     CLRBUSFREE      0x08
+       bit     CLRSCSIPERR     0x04
+       bit     CLRSTRB2FAST    0x02
+       bit     CLRREQINIT      0x01
+}
+
+/*
+ * SCSI Status 2
+ */
+register SSTAT2 {
+       address                 0x04d
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       mask    BUSFREETIME     0xc0
+       mask    BUSFREE_LQO     0x40
+       mask    BUSFREE_DFF0    0x80
+       mask    BUSFREE_DFF1    0xC0
+       bit     NONPACKREQ      0x20
+       bit     EXP_ACTIVE      0x10    /* SCSI Expander Active */
+       bit     BSYX            0x08    /* Busy Expander */
+       bit     WIDE_RES        0x04    /* Modes 0 and 1 only */
+       bit     SDONE           0x02    /* Modes 0 and 1 only */
+       bit     DMADONE         0x01    /* Modes 0 and 1 only */
+}
+
+/*
+ * Clear SCSI Interrupt 2
+ */
+register CLRSINT2 {
+       address                 0x04D
+       access_mode     WO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     CLRNONPACKREQ   0x20
+       bit     CLRWIDE_RES     0x04    /* Modes 0 and 1 only */
+       bit     CLRSDONE        0x02    /* Modes 0 and 1 only */
+       bit     CLRDMADONE      0x01    /* Modes 0 and 1 only */
+}
+
+/*
+ * SCSI Interrupt Mode 2
+ */
+register SIMODE2 {
+       address                 0x04D
+       access_mode     RW
+       modes           M_CFG
+       bit     ENWIDE_RES      0x04
+       bit     ENSDONE         0x02
+       bit     ENDMADONE       0x01
+}
+
+/*
+ * Physical Error Diagnosis
+ */
+register PERRDIAG {
+       address                 0x04E
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     HIZERO          0x80
+       bit     HIPERR          0x40
+       bit     PREVPHASE       0x20
+       bit     PARITYERR       0x10
+       bit     AIPERR          0x08
+       bit     CRCERR          0x04
+       bit     DGFORMERR       0x02
+       bit     DTERR           0x01
+}
+
+/*
+ * LQI Manager Current State
+ */
+register LQISTATE {
+       address                 0x04E
+       access_mode     RO
+       modes           M_CFG
+}
+
+/*
+ * SCSI Offset Count
+ */
+register SOFFCNT {
+       address                 0x04F
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+}
+
+/*
+ * LQO Manager Current State
+ */
+register LQOSTATE {
+       address                 0x04F
+       access_mode     RO
+       modes           M_CFG
+}
+
+/*
+ * LQI Manager Status
+ */
+register LQISTAT0 {
+       address                 0x050
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     LQIATNQAS       0x20
+       bit     LQICRCT1        0x10
+       bit     LQICRCT2        0x08
+       bit     LQIBADLQT       0x04
+       bit     LQIATNLQ        0x02
+       bit     LQIATNCMD       0x01
+}
+
+/*
+ * Clear LQI Interrupts 0
+ */
+register CLRLQIINTO {
+       address                 0x050
+       access_mode     WO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     CLRLQIATNQAS    0x20
+       bit     CLRLQICRCT1     0x10
+       bit     CLRLQICRCT2     0x08
+       bit     CLRLQIBADLQT    0x04
+       bit     CLRLQIATNLQ     0x02
+       bit     CLRLQIATNCMD    0x01
+}
+
+/*
+ * LQI Manager Interrupt Mode 0
+ */
+register LQIMODE0 {
+       address                 0x050
+       access_mode     RW
+       modes           M_CFG
+       bit     ENLQIATNQASK    0x20
+       bit     ENLQICRCT1      0x10
+       bit     ENLQICRCT2      0x08
+       bit     ENLQIBADLQT     0x04
+       bit     ENLQIATNLQ      0x02
+       bit     ENLQIATNCMD     0x01
+}
+
+/*
+ * LQI Manager Status 1
+ */
+register LQISTAT1 {
+       address                 0x051
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       mask    LQIPHASE_LQ     0x80
+       mask    LQIPHASE_NLQ    0x40
+       bit     LQIABORT        0x20
+       mask    LQICRCI_LQ      0x10
+       mask    LQICRCI_NLQ     0x08
+       bit     LQIBADLQI       0x04
+       mask    LQIOVERI_LQ     0x02
+       mask    LQIOVERI_NLQ    0x01
+}
+
+/*
+ * Clear LQI Manager Interrupts1
+ */
+register CLRLQIINT1 {
+       address                 0x051
+       access_mode     WO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       mask    CLRLQIPHASE_LQ  0x80
+       mask    CLRLQIPHASE_NLQ 0x40
+       bit     CLRLIQABORT     0x20
+       mask    CLRLQICRCI_LQ   0x10
+       mask    CLRLQICRCI_NLQ  0x08
+       bit     CLRLQIBADLQI    0x04
+       mask    CLRLQIOVERI_LQ  0x02
+       mask    CLRLQIOVERI_NLQ 0x01
+}
+
+/*
+ * LQI Manager Interrupt Mode 1
+ */
+register LQIMODE1 {
+       address                 0x051
+       access_mode     RW
+       modes           M_CFG
+       mask    ENLQIPHASE_LQ   0x80
+       mask    ENLQIPHASE_NLQ  0x40
+       bit     ENLIQABORT      0x20
+       mask    ENLQICRCI_LQ    0x10
+       mask    ENLQICRCI_NLQ   0x08
+       bit     ENLQIBADLQI     0x04
+       mask    ENLQIOVERI_LQ   0x02
+       mask    ENLQIOVERI_NLQ  0x01
+}
+
+/*
+ * LQI Manager Status 2
+ */
+register LQISTAT2 {
+       address                 0x052
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     PACKETIZED      0x80
+       bit     LQIPHASE_OUTPKT 0x40
+       bit     LQIWORKONLQ     0x20
+       bit     LQIWAITFIFO     0x10
+       bit     LQISTOPPKT      0x08
+       bit     LQISTOPLQ       0x04
+       bit     LQISTOPCMD      0x02
+       bit     LQIGSAVAIL      0x01
+}
+
+/*
+ * SCSI Status 3
+ */
+register SSTAT3 {
+       address                 0x053
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     NTRAMPERR       0x02
+       bit     OSRAMPERR       0x01
+}
+
+/*
+ * Clear SCSI Status 3
+ */
+register CLRSINT3 {
+       address                 0x053
+       access_mode     WO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     CLRNTRAMPERR    0x02
+       bit     CLROSRAMPERR    0x01
+}
+
+/*
+ * SCSI Interrupt Mode 3
+ */
+register SIMODE3 {
+       address                 0x053
+       access_mode     RW
+       modes           M_CFG
+       bit     ENNTRAMPERR     0x02
+       bit     ENOSRAMPERR     0x01
+}
+
+/*
+ * LQO Manager Status 0
+ */
+register LQOSTAT0 {
+       address                 0x054
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     LQOTARGSCBPERR  0x10
+       bit     LQOSTOPT2       0x08
+       bit     LQOATNLQ        0x04
+       bit     LQOATNPKT       0x02
+       bit     LQOTCRC         0x01
+}
+
+/*
+ * Clear LQO Manager interrupt 0
+ */
+register CLRLQOINT0 {
+       address                 0x054
+       access_mode     WO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     CLRLQOTARGSCBPERR       0x10
+       bit     CLRLQOSTOPT2            0x08
+       bit     CLRLQOATNLQ             0x04
+       bit     CLRLQOATNPKT            0x02
+       bit     CLRLQOTCRC              0x01
+}
+
+/*
+ * LQO Manager Interrupt Mode 0
+ */
+register LQOMODE0 {
+       address                 0x054
+       access_mode     RW
+       modes           M_CFG
+       bit     ENLQOTARGSCBPERR        0x10
+       bit     ENLQOSTOPT2             0x08
+       bit     ENLQOATNLQ              0x04
+       bit     ENLQOATNPKT             0x02
+       bit     ENLQOTCRC               0x01
+}
+
+/*
+ * LQO Manager Status 1
+ */
+register LQOSTAT1 {
+       address                 0x055
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     LQOINITSCBPERR  0x10
+       bit     LQOSTOPI2       0x08
+       bit     LQOBADQAS       0x04
+       bit     LQOBUSFREE      0x02
+       bit     LQOPHACHGINPKT  0x01
+}
+
+/*
+ * Clear LOQ Interrupt 1
+ */
+register CLRLQOINT1 {
+       address                 0x055
+       access_mode     WO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     CLRLQOINITSCBPERR       0x10
+       bit     CLRLQOSTOPI2            0x08
+       bit     CLRLQOBADQAS            0x04
+       bit     CLRLQOBUSFREE           0x02
+       bit     CLRLQOPHACHGINPKT       0x01
+}
+
+/*
+ * LQO Manager Interrupt Mode 1
+ */
+register LQOMODE1 {
+       address                 0x055
+       access_mode     RW
+       modes           M_CFG
+       bit     ENLQOINITSCBPERR        0x10
+       bit     ENLQOSTOPI2             0x08
+       bit     ENLQOBADQAS             0x04
+       bit     ENLQOBUSFREE            0x02
+       bit     ENLQOPHACHGINPKT        0x01
+}
+
+/*
+ * LQO Manager Status 2
+ */
+register LQOSTAT2 {
+       address                 0x056
+       access_mode     RO
+       modes           M_DFF0, M_DFF1, M_SCSI
+       mask    LQOPKT          0xE0
+       bit     LQOWAITFIFO     0x10
+       bit     LQOPHACHGOUTPKT 0x02    /* outside of packet boundaries. */
+       bit     LQOSTOP0        0x01    /* Stopped after sending all packets */
+}
+
+/*
+ * Output Synchronizer Space Count
+ */
+register OS_SPACE_CNT {
+       address                 0x056
+       access_mode     RO
+       modes           M_CFG
+}
+
+/*
+ * SCSI Interrupt Mode 1
+ * Setting any bit will enable the corresponding function
+ * in SIMODE1 to interrupt via the IRQ pin.
+ */
+register SIMODE1 {
+       address                 0x057
+       access_mode     RW
+       modes           M_DFF0, M_DFF1, M_SCSI
+       bit     ENSELTIMO       0x80
+       bit     ENATNTARG       0x40
+       bit     ENSCSIRST       0x20
+       bit     ENPHASEMIS      0x10
+       bit     ENBUSFREE       0x08
+       bit     ENSCSIPERR      0x04
+       bit     ENSTRB2FAST     0x02
+       bit     ENREQINIT       0x01
+}
+
+/*
+ * Good Status FIFO
+ */
+register GSFIFO {
+       address                 0x058
+       access_mode     RO
+       size            2
+       modes           M_DFF0, M_DFF1, M_SCSI
+}
+
+/*
+ * Data FIFO SCSI Transfer Control
+ */
+register DFFSXFRCTL {
+       address                 0x05A
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+       bit     CLRSHCNT        0x04
+       bit     CLRCHN          0x02
+       bit     RSTCHN          0x01
+}
+
+/*
+ * Next SCSI Control Block
+ */
+register NEXTSCB {
+       address                 0x05A
+       access_mode     RW
+       size            2
+       modes           M_SCSI
+}
+       
+/*
+ * SEQ Interrupts
+ */
+register SEQINTSRC {
+       address                 0x05B
+       access_mode     RO
+       modes           M_DFF0, M_DFF1
+       bit     CTXTDONE        0x40
+       bit     SAVEPTRS        0x20
+       bit     CFG4DATA        0x10
+       bit     CFG4ISTAT       0x08
+       bit     CFG4TSTAT       0x04
+       bit     CFG4ICMD        0x02
+       bit     CFG4TCMD        0x01
+}
+
+/*
+ * Clear Arp Interrupts
+ */
+register CLRSEQINTSRC {
+       address                 0x05B
+       access_mode     WO
+       modes           M_DFF0, M_DFF1
+       bit     CLRCTXTDONE     0x40
+       bit     CLRSAVEPTRS     0x20
+       bit     CLRCFG4DATA     0x10
+       bit     CLRCFG4ISTAT    0x08
+       bit     CLRCFG4TSTAT    0x04
+       bit     CLRCFG4ICMD     0x02
+       bit     CLRCFG4TCMD     0x01
+}
+
+/*
+ * SEQ Interrupt Enabled (Shared)
+ */
+register SEQIMODE {
+       address                 0x05C
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+       bit     ENCTXTDONE      0x40
+       bit     ENSAVEPTRS      0x20
+       bit     ENCFG4DATA      0x10
+       bit     ENCFG4ISTAT     0x08
+       bit     ENCFG4TSTAT     0x04
+       bit     ENCFG4ICMD      0x02
+       bit     ENCFG4TCMD      0x01
+}
+
+/*
+ * Current SCSI Control Block
+ */
+register CURRSCB {
+       address                 0x05C
+       access_mode     RW
+       size            2
+       modes           M_SCSI
+}
+
+/*
+ * Data FIFO Status
+ */
+register MDFFSTAT {
+       address                 0x05D
+       access_mode     RO
+       modes           M_DFF0, M_DFF1
+       bit     LASTSDONE       0x10
+       bit     SHVALID         0x08
+       bit     DLZERO          0x04 /* FIFO data ends on packet boundary. */
+       bit     DATAINFIFO      0x02
+       bit     FIFOFREE        0x01
+}
+
+/*
+ * CRC Control
+ */
+register CRCCONTROL {
+       address                 0x05d
+       access_mode     RW
+       modes           M_CFG
+       bit     CRCVALCHKEN             0x40
+}
+
+/*
+ * SCSI Test Control
+ */
+register SCSITEST {
+       address                 0x05E
+       access_mode     RW
+       modes           M_CFG
+       bit     CNTRTEST        0x08
+       bit     SEL_TXPLL_DEBUG 0x04
+}
+
+/*
+ * Data FIFO Queue Tag
+ */
+register DFFTAG {
+       address                 0x05E
+       access_mode     RW
+       size            2
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * Last SCSI Control Block
+ */
+register LASTSCB {
+       address                 0x05E
+       access_mode     RW
+       size            2
+       modes           M_SCSI
+}
+
+/*
+ * SCSI I/O Cell Power-down Control
+ */
+register IOPDNCTL {
+       address                 0x05F
+       access_mode     RW
+       modes           M_CFG
+       bit     DISABLE_OE      0x80
+       bit     PDN_IDIST       0x04
+       bit     PDN_DIFFSENSE   0x01
+}
+
+/*
+ * Shaddow Host Address.
+ */
+register SHADDR {
+       address                 0x060
+       access_mode     RO
+       size            8
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * Data Group CRC Interval.
+ */
+register DGRPCRCI {
+       address                 0x060
+       access_mode     RW
+       size            2
+       modes           M_CFG
+}
+
+/*
+ * Data Transfer Negotiation Address
+ */
+register NEGOADDR {
+       address                 0x060
+       access_mode     RW
+       modes           M_SCSI
+}
+
+/*
+ * Data Transfer Negotiation Data - Period Byte
+ */
+register NEGPERIOD {
+       address                 0x061
+       access_mode     RW
+       modes           M_SCSI
+}
+
+/*
+ * Packetized CRC Interval
+ */
+register PACKCRCI {
+       address                 0x062
+       access_mode     RW
+       size            2
+       modes           M_CFG
+}
+
+/*
+ * Data Transfer Negotiation Data - Offset Byte
+ */
+register NEGOFFSET {
+       address                 0x062
+       access_mode     RW
+       modes           M_SCSI
+}
+
+/*
+ * Data Transfer Negotiation Data - PPR Options
+ */
+register NEGPPROPTS {
+       address                 0x063
+       access_mode     RW
+       modes           M_SCSI
+       bit     PPROPT_PACE     0x08
+       bit     PPROPT_QAS      0x04
+       bit     PPROPT_DT       0x02
+       bit     PPROPT_IUT      0x01
+}
+
+/*
+ * Data Transfer Negotiation Data -  Connection Options
+ */
+register NEGCONOPTS {
+       address                 0x064
+       access_mode     RW
+       modes           M_SCSI
+       bit     ENAIP           0x08
+       bit     ENAUTOATNI      0x04
+       bit     ENAUTOATNO      0x02
+       bit     WIDEXFER        0x01
+}
+
+/*
+ * Negotiation Table Annex Column Index.
+ */
+register ANNEXCOL {
+       address                 0x065
+       access_mode     RW
+       modes           M_SCSI
+}
+
+const AHD_ANNEXCOL_PRECOMP     4
+const  AHD_PRECOMP_MASK        0x07
+const  AHD_PRECOMP_CUTBACK_17  0x04
+const  AHD_PRECOMP_CUTBACK_29  0x06
+const  AHD_PRECOMP_CUTBACK_37  0x07
+const  AHD_PRECOMP_FASTSLEW    0x40
+const AHD_NUM_ANNEXCOLS                4
+
+/*
+ * Negotiation Table Annex Data Port.
+ */
+register ANNEXDAT {
+       address                 0x066
+       access_mode     RW
+       modes           M_SCSI
+}
+
+/*
+ * Initiator's Own Id.
+ * The SCSI ID to use for Selection Out and seen during a reselection..
+ */
+register IOWNID {
+       address                 0x067
+       access_mode     RW
+       modes           M_SCSI
+}
+
+/*
+ * 960MHz Phase-Locked Loop Control 0
+ */
+register PLL960CTL0 {
+       address                 0x068
+       access_mode     RW
+       modes           M_CFG
+       bit     PLL_VCOSEL      0x80
+       bit     PLL_PWDN        0x40
+       mask    PLL_NS          0x30
+       bit     PLL_ENLUD       0x08
+       bit     PLL_ENLPF       0x04
+       bit     PLL_DLPF        0x02
+       bit     PLL_ENFBM       0x01
+}
+
+/*
+ * Target Own Id
+ */
+register TOWNID {
+       address                 0x069
+       access_mode     RW
+       modes           M_SCSI
+}
+
+/*
+ * 960MHz Phase-Locked Loop Control 1
+ */
+register PLL960CTL1 {
+       address                 0x069
+       access_mode     RW
+       modes           M_CFG
+       bit     PLL_CNTEN       0x80
+       bit     PLL_CNTCLR      0x40
+       bit     PLL_RST         0x01
+}
+
+/*
+ * Expander Signature
+ */
+register XSIG {
+       address                 0x06A
+       access_mode     RW
+       modes           M_SCSI
+}
+
+/*
+ * Shadow Byte Count
+ */
+register SHCNT {
+       address                 0x068
+       access_mode     RW
+       size            3
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * Selection Out ID
+ */
+register SELOID {
+       address                 0x06B
+       access_mode     RW
+       modes           M_SCSI
+}
+
+/*
+ * 960-MHz Phase-Locked Loop Test Count
+ */
+register PLL960CNT0 {
+       address                 0x06A
+       access_mode     RO
+       size            2
+       modes           M_CFG
+}
+
+/*
+ * 400-MHz Phase-Locked Loop Control 0
+ */
+register PLL400CTL0 {
+       address                 0x06C
+       access_mode     RW
+       modes           M_CFG
+       bit     PLL_VCOSEL      0x80
+       bit     PLL_PWDN        0x40
+       mask    PLL_NS          0x30
+       bit     PLL_ENLUD       0x08
+       bit     PLL_ENLPF       0x04
+       bit     PLL_DLPF        0x02
+       bit     PLL_ENFBM       0x01
+}
+
+/*
+ * Arbitration Fairness
+ */
+register FAIRNESS {
+       address                 0x06C
+       access_mode     RW
+       size            2
+       modes           M_SCSI
+}
+
+/*
+ * 400-MHz Phase-Locked Loop Control 1
+ */
+register PLL400CTL1 {
+       address                 0x06D
+       access_mode     RW
+       modes           M_CFG
+       bit     PLL_CNTEN       0x80
+       bit     PLL_CNTCLR      0x40
+       bit     PLL_RST         0x01
+}
+
+/*
+ * Arbitration Unfairness
+ */
+register UNFAIRNESS {
+       address                 0x06E
+       access_mode     RW
+       size            2
+       modes           M_SCSI
+}
+
+/*
+ * 400-MHz Phase-Locked Loop Test Count
+ */
+register PLL400CNT0 {
+       address                 0x06E
+       access_mode     RO
+       size            2
+       modes           M_CFG
+}
+
+/*
+ * SCB Page Pointer
+ */
+register SCBPTR {
+       address                 0x0A8
+       access_mode     RW
+       size            2
+       modes           M_DFF0, M_DFF1, M_CCHAN, M_SCSI
+}
+
+/*
+ * CMC SCB Array Count
+ * Number of bytes to transfer between CMC SCB memory and SCBRAM.
+ * Transfers must be 8byte aligned and sized.
+ */
+register CCSCBACNT {
+       address                 0x0AB
+       access_mode     RW
+       modes           M_CCHAN
+}
+
+/*
+ * SCB Autopointer
+ * SCB-Next Address Snooping logic.  When an SCB is transferred to
+ * the card, the next SCB address to be used by the CMC array can
+ * be autoloaded from that transfer.
+ */
+register SCBAUTOPTR {
+       address                 0x0AB
+       access_mode     RW
+       modes           M_CFG
+       bit     AUSCBPTR_EN     0x80
+       mask    SCBPTR_ADDR     0x38
+       mask    SCBPTR_OFF      0x07
+}
+
+/*
+ * CMC SG Ram Address Pointer
+ */
+register CCSGADDR {
+       address                 0x0AC
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * CMC SCB RAM Address Pointer
+ */
+register CCSCBADDR {
+       address                 0x0AC
+       access_mode     RW
+       modes           M_CCHAN
+}
+
+/*
+ * CMC SCB Ram Back-up Address Pointer
+ * Indicates the true stop location of transfers halted prior
+ * to SCBHCNT going to 0.
+ */
+register CCSCBADR_BK {
+       address                 0x0AC
+       access_mode     RO
+       modes           M_CFG
+}
+
+/*
+ * CMC SG Control
+ */
+register CCSGCTL {
+       address                 0x0AD
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+       bit     CCSGDONE        0x80
+       bit     SG_CACHE_AVAIL  0x10
+       bit     CCSGEN          0x08
+       bit     SG_FETCH_REQ    0x02
+       bit     CCSGRESET       0x01
+}
+
+/*
+ * CMD SCB Control
+ */
+register CCSCBCTL {
+       address                 0x0AD
+       access_mode     RW
+       modes           M_CCHAN
+       bit     CCSCBDONE       0x80
+       bit     ARRDONE         0x40
+       bit     CCARREN         0x10
+       bit     CCSCBEN         0x08
+       bit     CCSCBDIR        0x04
+       bit     CCSCBRESET      0x01
+}
+
+/*
+ * CMC Ram BIST
+ */
+register CMC_RAMBIST {
+       address                 0x0AD
+       access_mode     RW
+       modes           M_CFG
+       bit     SG_ELEMENT_SIZE         0x80
+       bit     SCBRAMBIST_FAIL         0x40
+       bit     SG_BIST_FAIL            0x20
+       bit     SG_BIST_EN              0x10
+       bit     CMC_BUFFER_BIST_FAIL    0x02
+       bit     CMC_BUFFER_BIST_EN      0x01
+}
+
+/*
+ * CMC SG RAM Data Port
+ */
+register CCSGRAM {
+       address                 0x0B0
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * CMC SCB RAM Data Port
+ */
+register CCSCBRAM {
+       address                 0x0B0
+       access_mode     RW
+       modes           M_CCHAN
+}
+
+/*
+ * Flex DMA Address.
+ */
+register FLEXADR {
+       address                 0x0B0
+       access_mode     RW
+       size            3
+       modes           M_SCSI
+}
+
+/*
+ * Flex DMA Byte Count
+ */
+register FLEXCNT {
+       address                 0x0B3
+       access_mode     RW
+       size            2
+       modes           M_SCSI
+}
+
+/*
+ * Flex DMA Status
+ */
+register FLEXDMASTAT {
+       address                 0x0B5
+       access_mode     RW
+       modes           M_SCSI
+       bit     FLEXDMAERR      0x02
+       bit     FLEXDMADONE     0x01
+}
+
+/*
+ * Flex DMA Data Port
+ */
+register FLEXDATA {
+       address                 0x0B6
+       access_mode     RW
+       modes           M_SCSI
+}
+
+/*
+ * Board Data
+ */
+register BRDDAT {
+       address                 0x0B8
+       access_mode     RW
+       modes           M_SCSI
+}
+
+/*
+ * Board Control
+ */
+register BRDCTL {
+       address                 0x0B9
+       access_mode     RW
+       modes           M_SCSI
+       bit     FLXARBACK       0x80
+       bit     FLXARBREQ       0x40
+       mask    BRDADDR         0x38
+       bit     BRDEN           0x04
+       bit     BRDRW           0x02
+       bit     BRDSTB          0x01
+}
+
+/*
+ * Serial EEPROM Address
+ */
+register SEEADR {
+       address                 0x0BA
+       access_mode     RW
+       modes           M_SCSI
+}
+
+/*
+ * Serial EEPROM Data
+ */
+register SEEDAT {
+       address                 0x0BC
+       access_mode     RW
+       size            2
+       modes           M_SCSI
+}
+
+/*
+ * Serial EEPROM Status
+ */
+register SEESTAT {
+       address                 0x0BE
+       access_mode     RO
+       modes           M_SCSI
+       bit     INIT_DONE       0x80
+       mask    SEEOPCODE       0x70
+       bit     LDALTID_L       0x08
+       bit     SEEARBACK       0x04
+       bit     SEEBUSY         0x02
+       bit     SEESTART        0x01
+}
+
+/*
+ * Serial EEPROM Control
+ */
+register SEECTL {
+       address                 0x0BE
+       access_mode     RW
+       modes           M_SCSI
+       mask    SEEOPCODE       0x70
+       mask    SEEOP_ERASE     0x70
+       mask    SEEOP_READ      0x60
+       mask    SEEOP_WRITE     0x50
+       /*
+        * The following four commands use special
+        * addresses for differentiation.
+        */
+       mask    SEEOP_ERAL      0x40
+       mask    SEEOP_EWEN      0x40
+       mask    SEEOP_WALL      0x40
+       mask    SEEOP_EWDS      0x40
+       bit     SEERST          0x02
+       bit     SEESTART        0x01
+}
+
+const SEEOP_ERAL_ADDR  0x80
+const SEEOP_EWEN_ADDR  0xC0
+const SEEOP_WRAL_ADDR  0x40
+const SEEOP_EWDS_ADDR  0x00
+
+/*
+ * SCB Counter
+ */
+register SCBCNT {
+       address                 0x0BF
+       access_mode     RW
+       modes           M_SCSI
+}
+
+/*
+ * Data FIFO Write Address
+ * Pointer to the next QWD location to be written to the data FIFO.
+ */
+register DFWADDR {
+       address                 0x0C0
+       access_mode     RW
+       size            2
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * DSP Filter Control
+ */
+register DSPFLTRCTL {
+       address                 0x0C0
+       access_mode     RW
+       modes           M_CFG
+       bit     FLTRDISABLE     0x20
+       bit     EDGESENSE       0x10
+       mask    DSPFCNTSEL      0x0F
+}
+
+/*
+ * DSP Data Channel Control
+ */
+register DSPDATACTL {
+       address                 0x0C1
+       access_mode     RW
+       modes           M_CFG
+       bit     BYPASSENAB      0x80
+       bit     DESQDIS         0x10
+       bit     RCVROFFSTDIS    0x04
+       bit     XMITOFFSTDIS    0x02
+}
+
+/*
+ * Data FIFO Read Address
+ * Pointer to the next QWD location to be read from the data FIFO.
+ */
+register DFRADDR {
+       address                 0x0C2
+       access_mode     RW
+       size            2
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * DSP REQ Control
+ */
+register DSPREQCTL {
+       address                 0x0C2
+       access_mode     RW
+       modes           M_CFG
+       mask    MANREQCTL       0xC0
+       mask    MANREQDLY       0x3F
+}
+
+/*
+ * DSP ACK Control
+ */
+register DSPACKCTL {
+       address                 0x0C3
+       access_mode     RW
+       modes           M_CFG
+       mask    MANACKCTL       0xC0
+       mask    MANACKDLY       0x3F
+}
+
+/*
+ * Data FIFO Data
+ * Read/Write byte port into the data FIFO.  The read and write
+ * FIFO pointers increment with each read and write respectively
+ * to this port.
+ */
+register DFDAT {
+       address                 0x0C4
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * DSP Channel Select
+ */
+register DSPSELECT {
+       address                 0x0C4
+       access_mode     RW
+       modes           M_CFG
+       bit     AUTOINCEN       0x80
+       mask    DSPSEL          0x1F
+}
+
+const NUMDSPS 0x14
+
+/*
+ * Write Bias Control
+ */
+register WRTBIASCTL {
+       address                 0x0C5
+       access_mode     WO
+       modes           M_CFG
+       bit     AUTOXBCDIS      0x80
+       mask    XMITMANVAL      0x3F
+}
+
+const WRTBIASCTL_CPQ_DEFAULT 0x97
+
+/*
+ * Receiver Bias Control
+ */
+register RCVRBIOSCTL {
+       address                 0x0C6
+       access_mode     WO
+       modes           M_CFG
+       bit     AUTORBCDIS      0x80
+       mask    RCVRMANVAL      0x3F
+}
+
+/*
+ * Write Bias Calculator
+ */
+register WRTBIASCALC {
+       address                 0x0C7
+       access_mode     RO
+       modes           M_CFG
+}
+
+/*
+ * Data FIFO Pointers
+ * Contains the byte offset from DFWADDR and DWRADDR to the current
+ * FIFO write/read locations.
+ */
+register DFPTRS {
+       address                 0x0C8
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * Receiver Bias Calculator
+ */
+register RCVRBIASCALC {
+       address                 0x0C8
+       access_mode     RO
+       modes           M_CFG
+}
+
+/*
+ * Data FIFO Debug Control
+ */
+register DFDBCTL {
+       address                         0x0C8
+       access_mode     RW
+       modes           M_DFF0, M_DFF1
+       bit     DFF_CIO_WR_RDY          0x20
+       bit     DFF_CIO_RD_RDY          0x10
+       bit     DFF_DIR_ERR             0x08
+       bit     DFF_RAMBIST_FAIL        0x04
+       bit     DFF_RAMBIST_DONE        0x02
+       bit     DFF_RAMBIST_EN          0x01
+}
+
+/*
+ * Data FIFO Backup Read Pointer
+ * Contains the data FIFO address to be restored if the last
+ * data accessed from the data FIFO was not transferred successfully.
+ */
+register DFBKPTR {
+       address                 0x0C9
+       access_mode     RW
+       size            2
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * Skew Calculator
+ */
+register SKEWCALC {
+       address                 0x0C9
+       access_mode     RO
+       modes           M_CFG
+}
+
+/*
+ * Data FIFO Space Count
+ * Number of FIFO locations that are free.
+ */
+register DFSCNT {
+       address                 0x0CC
+       access_mode     RO
+       size            2
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * Data FIFO Byte Count
+ * Number of filled FIFO locations.
+ */
+register DFBCNT {
+       address                 0x0CE
+       access_mode     RO
+       size            2
+       modes           M_DFF0, M_DFF1
+}
+
+/*
+ * Sequencer Program Overlay Address.
+ * Low address must be written prior to high address.
+ */
+register OVLYADDR {
+       address                 0x0D4
+       modes           M_SCSI
+       size            2
+       access_mode     RW
+}
+
+/*
+ * Sequencer Control 0
+ * Error detection mode, speed configuration,
+ * single step, breakpoints and program load.
+ */
+register SEQCTL0 {
+       address                 0x0D6
+       access_mode RW
+       bit     PERRORDIS       0x80
+       bit     PAUSEDIS        0x40
+       bit     FAILDIS         0x20
+       bit     FASTMODE        0x10
+       bit     BRKADRINTEN     0x08
+       bit     STEP            0x04
+       bit     SEQRESET        0x02
+       bit     LOADRAM         0x01
+}
+
+/*
+ * Sequencer Control 1
+ * Instruction RAM Diagnostics
+ */
+register SEQCTL1 {
+       address                 0x0D7
+       access_mode RW
+       bit     OVRLAY_DATA_CHK 0x08
+       bit     RAMBIST_DONE    0x04
+       bit     RAMBIST_FAIL    0x02
+       bit     RAMBIST_EN      0x01
+}
+
+/*
+ * Sequencer Flags
+ * Zero and Carry state of the ALU.
+ */
+register FLAGS {
+       address                 0x0D8
+       access_mode RO
+       bit     ZERO            0x02
+       bit     CARRY           0x01
+}
+
+/*
+ * Sequencer Interrupt Control
+ */ 
+register SEQINTCTL {
+       address                 0x0D9
+       access_mode RW
+       bit     INTVEC1DSL      0x80
+       bit     INT1_CONTEXT    0x20
+       bit     SCS_SEQ_INT1M1  0x10
+       bit     SCS_SEQ_INT1M0  0x08
+       mask    INTMASK         0x06
+       bit     IRET            0x01
+}
+
+/*
+ * Sequencer RAM Data Port
+ * Single byte window into the Sequencer Instruction Ram area starting
+ * at the address specified by OVLYADDR.  To write a full instruction word,
+ * simply write four bytes in succession.  OVLYADDR will increment after the
+ * most significant instrution byte (the byte with the parity bit) is written.
+ */
+register SEQRAM {
+       address                 0x0DA
+       access_mode RW
+}
+
+/*
+ * Sequencer Program Counter
+ * Low byte must be written prior to high byte.
+ */
+register PRGMCNT {
+       address                 0x0DE
+       access_mode     RW
+       size            2
+}
+
+/*
+ * Accumulator
+ */
+register ACCUM {
+       address                 0x0E0
+       access_mode RW
+       accumulator
+}
+
+/*
+ * Source Index Register
+ * Incrementing index for reads of SINDIR and the destination (low byte only)
+ * for any immediate operands passed in jmp, jc, jnc, call instructions.
+ * Example:
+ *             mvi     0xFF    call some_routine;
+ *
+ *  Will set SINDEX[0] to 0xFF and call the routine "some_routine.
+ */
+register SINDEX        {
+       address                 0x0E2
+       access_mode     RW
+       size            2
+       sindex
+}
+
+/*
+ * Destination Index Register
+ * Incrementing index for writes to DINDIR.  Can be used as a scratch register.
+ */
+register DINDEX {
+       address                 0x0E4
+       access_mode     RW
+       size            2
+}
+
+/*
+ * Break Address
+ * Sequencer instruction breakpoint address address.
+ */
+register BRKADDR0 {
+       address                 0x0E6
+       access_mode     RW
+}
+
+register BRKADDR1 {
+       address                 0x0E6
+       access_mode     RW
+       bit     BRKDIS          0x80    /* Disable Breakpoint */
+}
+
+/*
+ * All Ones
+ * All reads to this register return the value 0xFF.
+ */
+register ALLONES {
+       address                 0x0E8
+       access_mode RO
+       allones
+}
+
+/*
+ * All Zeros
+ * All reads to this register return the value 0.
+ */
+register ALLZEROS {
+       address                 0x0EA
+       access_mode RO
+       allzeros
+}
+
+/*
+ * No Destination
+ * Writes to this register have no effect.
+ */
+register NONE {
+       address                 0x0EA
+       access_mode WO
+       none
+}
+
+/*
+ * Source Index Indirect
+ * Reading this register is equivalent to reading (register_base + SINDEX) and
+ * incrementing SINDEX by 1.
+ */
+register SINDIR        {
+       address                 0x0EC
+       access_mode RO
+}
+
+/*
+ * Destination Index Indirect
+ * Writing this register is equivalent to writing to (register_base + DINDEX)
+ * and incrementing DINDEX by 1.
+ */
+register DINDIR         {
+       address                 0x0ED
+       access_mode WO
+}
+
+/*
+ * Function One
+ * 2's complement to bit value conversion.  Write the 2's complement value
+ * (0-7 only) to the top nibble and retrieve the bit indexed by that value
+ * on the next read of this register. 
+ * Example:
+ *     Write   0x60
+ *     Read    0x40
+ */
+register FUNCTION1 {
+       address                 0x0F0
+       access_mode RW
+}
+
+/*
+ * Stack
+ * Window into the stack.  Each stack location is 10 bits wide reported
+ * low byte followed by high byte.  There are 8 stack locations.
+ */
+register STACK {
+       address                 0x0F2
+       access_mode RW
+}
+
+/*
+ * Interrupt Vector 1 Address
+ * Interrupt branch address for SCS SEQ_INT1 mode 0 and 1 interrupts.
+ */
+register INTVEC1_ADDR {
+       address                 0x0F4
+       access_mode     RW
+       size            2
+       modes           M_CFG
+}
+
+/*
+ * Current Address
+ * Address of the SEQRAM instruction currently executing instruction.
+ */
+register CURADDR {
+       address                 0x0F4
+       access_mode     RW
+       size            2
+       modes           M_SCSI
+}
+
+/*
+ * Interrupt Vector 2 Address
+ * Interrupt branch address for HST_SEQ_INT2 interrupts.
+ */
+register INTVEC2_ADDR {
+       address                 0x0F6
+       access_mode     RW
+       size            2
+       modes           M_CFG
+}
+
+/*
+ * Last Address
+ * Address of the SEQRAM instruction executed prior to the current instruction.
+ */
+register LASTADDR {
+       address                 0x0F6
+       access_mode     RW
+       size            2
+       modes           M_SCSI
+}
+
+register AHD_PCI_CONFIG_BASE {
+       address                 0x100
+       access_mode     RW
+       size            256
+       modes           M_CFG
+}
+
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+scratch_ram {
+       /* Mode Specific */
+       address                 0x0A0
+       size    8
+       modes   0, 1, 2, 3
+       REG0 {
+               size            2
+       }
+       REG1 {
+               size            2
+       }
+       REG2 {
+               size            2
+       }
+       SG_STATE {
+               size            1
+               bit     SEGS_AVAIL      0x01
+               bit     LOADING_NEEDED  0x02
+               bit     FETCH_INPROG    0x04
+       }
+       /*
+        * Track whether the transfer byte count for
+        * the current data phase is odd.
+        */
+       DATA_COUNT_ODD {
+               size            1
+       }
+}
+
+scratch_ram {
+       /* Mode Specific */
+       address                 0x0F8
+       size    8
+       modes   0, 1, 2, 3
+       LONGJMP_ADDR {
+               size            2
+       }
+       LONGJMP_SCB {
+               size            2
+       }
+       ACCUM_SAVE {
+               size            1
+       }
+}
+
+
+scratch_ram {
+       address                 0x100
+       size    128
+       modes   0, 1, 2, 3
+       /*
+        * Per "other-id" execution queues.  We use an array of
+        * tail pointers into lists of SCBs sorted by "other-id".
+        * The execution head pointer threads the head SCBs for
+        * each list.
+        */
+       WAITING_SCB_TAILS {
+               size            32
+       }
+       WAITING_TID_HEAD {
+               size            2
+       }
+       WAITING_TID_TAIL {
+               size            2
+       }
+       /*
+        * SCBID of the next SCB in the new SCB queue.
+        */
+       NEXT_QUEUED_SCB_ADDR {
+               size            4
+       }
+       /*
+        * head of list of SCBs that have
+        * completed but have not been
+        * put into the qoutfifo.
+        */
+       COMPLETE_SCB_HEAD {
+               size            2
+       }
+       /*
+        * The list of completed SCBs in
+        * the active DMA.
+        */
+       COMPLETE_SCB_DMAINPROG_HEAD {
+               size            2
+       }
+       /*
+        * head of list of SCBs that have
+        * completed but need to be uploaded
+        * to the host prior to being completed.
+        */
+       COMPLETE_DMA_SCB_HEAD {
+               size            2
+       }
+       /* Counting semaphore to prevent new select-outs */
+       QFREEZE_COUNT {
+               size            2
+       }
+       /*
+        * Mode to restore on idle_loop exit.
+        */
+       SAVED_MODE {
+               size            1
+       }
+       /*
+        * Single byte buffer used to designate the type or message
+        * to send to a target.
+        */
+       MSG_OUT {
+               size            1
+       }
+       /* Parameters for DMA Logic */
+       DMAPARAMS {
+               size            1
+               bit     PRELOADEN       0x80
+               bit     WIDEODD         0x40
+               bit     SCSIEN          0x20
+               bit     SDMAEN          0x10
+               bit     SDMAENACK       0x10
+               bit     HDMAEN          0x08
+               bit     HDMAENACK       0x08
+               bit     DIRECTION       0x04    /* Set indicates PCI->SCSI */
+               bit     FIFOFLUSH       0x02
+               bit     FIFORESET       0x01
+       }
+       SEQ_FLAGS {
+               size            1
+               bit     NOT_IDENTIFIED          0x80
+               bit     TARGET_CMD_IS_TAGGED    0x40
+               bit     NO_CDB_SENT             0x40
+               bit     DPHASE                  0x20
+               /* Target flags */
+               bit     TARG_CMD_PENDING        0x10
+               bit     CMDPHASE_PENDING        0x08
+               bit     DPHASE_PENDING          0x04
+               bit     SPHASE_PENDING          0x02
+               bit     NO_DISCONNECT           0x01
+       }
+       /*
+        * Temporary storage for the
+        * target/channel/lun of a
+        * reconnecting target
+        */
+       SAVED_SCSIID {
+               size            1
+       }
+       SAVED_LUN {
+               size            1
+       }
+       /*
+        * The last bus phase as seen by the sequencer. 
+        */
+       LASTPHASE {
+               size            1
+               bit     CDI             0x80
+               bit     IOI             0x40
+               bit     MSGI            0x20
+               mask    PHASE_MASK      CDI|IOI|MSGI
+               mask    P_DATAOUT       0x00
+               mask    P_DATAIN        IOI
+               mask    P_DATAOUT_DT    P_DATAOUT|MSGO
+               mask    P_DATAIN_DT     P_DATAIN|MSGO
+               mask    P_COMMAND       CDI
+               mask    P_MESGOUT       CDI|MSGI
+               mask    P_STATUS        CDI|IOI
+               mask    P_MESGIN        CDI|IOI|MSGI
+               mask    P_BUSFREE       0x01
+       }
+       /*
+        * Base address of our shared data with the kernel driver in host
+        * memory.  This includes the qoutfifo and target mode
+        * incoming command queue.
+        */
+       SHARED_DATA_ADDR {
+               size            4
+       }
+       /*
+        * Pointer to location in host memory for next
+        * position in the qoutfifo.
+        */
+       QOUTFIFO_NEXT_ADDR {
+               size            4
+       }
+       /*
+        * Kernel and sequencer offsets into the queue of
+        * incoming target mode command descriptors.  The
+        * queue is full when the KERNEL_TQINPOS == TQINPOS.
+        */
+       KERNEL_TQINPOS {
+               size            1
+       }
+       TQINPOS {                
+               size            1
+       }
+       ARG_1 {
+               size            1
+               mask    SEND_MSG                0x80
+               mask    SEND_SENSE              0x40
+               mask    SEND_REJ                0x20
+               mask    MSGOUT_PHASEMIS         0x10
+               mask    EXIT_MSG_LOOP           0x08
+               mask    CONT_MSG_LOOP_WRITE     0x04
+               mask    CONT_MSG_LOOP_READ      0x03
+               mask    CONT_MSG_LOOP_TARG      0x02
+               alias   RETURN_1
+       }
+       ARG_2 {
+               size            1
+               alias   RETURN_2
+       }
+
+       /*
+        * Snapshot of MSG_OUT taken after each message is sent.
+        */
+       LAST_MSG {
+               size            1
+       }
+
+       /*
+        * Sequences the kernel driver has okayed for us.  This allows
+        * the driver to do things like prevent initiator or target
+        * operations.
+        */
+       SCSISEQ_TEMPLATE {
+               size            1
+               bit     MANUALCTL       0x40
+               bit     ENSELI          0x20
+               bit     ENRSELI         0x10
+               mask    MANUALP         0x0C
+               bit     ENAUTOATNP      0x02
+               bit     ALTSTIM         0x01
+       }
+
+       /*
+        * The initiator specified tag for this target mode transaction.
+        */
+       INITIATOR_TAG {
+               size            1
+       }
+
+       SEQ_FLAGS2 {
+               size            1
+               bit     SCB_DMA                   0x01
+               bit     TARGET_MSG_PENDING        0x02
+               bit     SELECTOUT_QFROZEN         0x04
+       }
+       /*
+        * Target-mode CDB type to CDB length table used
+        * in non-packetized operation.
+        */
+       CMDSIZE_TABLE {
+               size            8
+       }
+}
+
+/************************* Hardware SCB Definition ****************************/
+scb {
+       address                 0x180
+       size    64
+       modes   0, 1, 2, 3
+       SCB_RESIDUAL_DATACNT {
+               size    4
+               alias   SCB_CDB_STORE
+       }
+       SCB_RESIDUAL_SGPTR {
+               size    4
+               alias   SCB_CDB_PTR
+               mask    SG_ADDR_MASK            0xf8    /* In the last byte */
+               bit     SG_OVERRUN_RESID        0x02    /* In the first byte */
+               bit     SG_LIST_NULL            0x01    /* In the first byte */
+       }
+       SCB_SCSI_STATUS {
+               size    1
+       }
+       SCB_TARGET_PHASES {
+               size    1
+       }
+       SCB_TARGET_DATA_DIR {
+               size    1
+       }
+       SCB_TARGET_ITAG {
+               size    1
+       }
+       SCB_SENSE_BUSADDR {
+               /*
+                * Only valid if CDB length is less than 13 bytes or
+                * we are using a CDB pointer.  Otherwise contains
+                * the last 4 bytes of embedded cdb information.
+                */
+               size    4
+               alias   SCB_NEXT_COMPLETE
+       }
+       SCB_CDB_LEN {
+               size    1
+               bit     SCB_CDB_LEN_PTR 0x80    /* CDB in host memory */
+       }
+       SCB_TASK_MANAGEMENT {
+               size    1
+       }
+       SCB_TAG {
+               size    2
+       }
+       SCB_NEXT {
+               alias   SCB_NEXT_SCB_BUSADDR
+               size    2
+       }
+       SCB_NEXT2 {
+               size    2
+       }
+       SCB_DATAPTR {
+               size    8
+       }
+       SCB_DATACNT {
+               /*
+                * The last byte is really the high address bits for
+                * the data address.
+                */
+               size    4
+               bit     SG_LAST_SEG             0x80    /* In the fourth byte */
+               mask    SG_HIGH_ADDR_BITS       0x7F    /* In the fourth byte */
+       }
+       SCB_SGPTR {
+               size    4
+               bit     SG_STATUS_VALID 0x04    /* In the first byte */
+               bit     SG_FULL_RESID   0x02    /* In the first byte */
+               bit     SG_LIST_NULL    0x01    /* In the first byte */
+       }
+       SCB_CONTROL {
+               size    1
+               bit     TARGET_SCB      0x80
+               bit     DISCENB         0x40
+               bit     TAG_ENB         0x20
+               bit     MK_MESSAGE      0x10
+               bit     STATUS_RCVD     0x08
+               bit     DISCONNECTED    0x04
+               mask    SCB_TAG_TYPE    0x03
+       }
+       SCB_SCSIID {
+               size    1
+               mask    TID     0xF0
+               mask    OID     0x0F
+       }
+       SCB_LUN {
+               size    1
+               mask    LID                             0xff
+       }
+       SCB_TASK_ATTRIBUTE {
+               size    1
+               alias   SCB_NONPACKET_TAG
+       }
+       SCB_BUSADDR {
+               size    4
+       }
+       SCB_DISCONNECTED_LISTS {
+               size    16
+       }
+}
+
+/*********************************** Constants ********************************/
+const SEQ_STACK_SIZE   8
+const MK_MESSAGE_BIT_OFFSET    4
+const TID_SHIFT                4
+const TARGET_CMD_CMPLT 0xfe
+const INVALID_ADDR     0x80
+#define SCB_LIST_NULL  0xff
+
+const CCSGADDR_MAX     0x80
+const CCSCBADDR_MAX    0x80
+const CCSGRAM_MAXSEGS  16
+
+/* Selection Timeout Timer Constants */
+const STIMESEL_SHIFT   3
+const STIMESEL_MIN     0x18
+const STIMESEL_BUG_ADJ 0x8
+
+/* WDTR Message values */
+const BUS_8_BIT                        0x00
+const BUS_16_BIT               0x01
+const BUS_32_BIT               0x02
+
+/* Offset maximums */
+const MAX_OFFSET               0xfe
+const MAX_OFFSET_PACED         0x7f
+const HOST_MSG                 0xff
+
+/*
+ * The size of our sense buffers.
+ * Sense buffer mapping can be handled in either of two ways.
+ * The first is to allocate a dmamap for each transaction.
+ * Depending on the architecture, dmamaps can be costly. The
+ * alternative is to statically map the buffers in much the same
+ * way we handle our scatter gather lists.  The driver implements
+ * the later.
+ */
+const AHD_SENSE_BUFSIZE                256
+
+/* Target mode command processing constants */
+const CMD_GROUP_CODE_SHIFT     0x05
+
+const STATUS_BUSY              0x08
+const STATUS_QUEUE_FULL                0x28
+const STATUS_PKT_SENSE         0xFF
+const TARGET_DATA_IN           1
+
+const SCB_TRANSFER_SIZE                48
+/* PKT_OVERRUN_BUFSIZE must be a multiple of 256 less than 64K */
+const PKT_OVERRUN_BUFSIZE      512
+
+/*
+ * Downloaded (kernel inserted) constants
+ */
+const SG_PREFETCH_CNT download
+const SG_PREFETCH_CNT_LIMIT download
+const SG_PREFETCH_ALIGN_MASK download
+const SG_PREFETCH_ADDR_MASK download
+const SG_SIZEOF download
+const PKT_OVERRUN_BUFOFFSET download
+
+/*
+ * BIOS SCB offsets
+ */
+const NVRAM_SCB_OFFSET 0x2C
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx.seq b/xen/drivers/scsi/aic7xxx/aic79xx.seq
new file mode 100644 (file)
index 0000000..0fb6575
--- /dev/null
@@ -0,0 +1,1723 @@
+/*
+ * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#42 $"
+PATCH_ARG_LIST = "struct ahd_softc *ahd"
+
+#include "aic79xx.reg"
+#include "scsi_message.h"
+
+idle_loop:
+       SET_MODE(M_SCSI, M_SCSI);
+       test    SCSISEQ0, ENSELO|ENARBO jnz idle_loop_checkbus;
+       test    SEQ_FLAGS2, SELECTOUT_QFROZEN jnz idle_loop_checkbus;
+       cmp     WAITING_TID_HEAD[1], SCB_LIST_NULL je idle_loop_checkbus;
+       /*
+        * ENSELO is cleared by a SELDO, so we must test for SELDO
+        * one last time.
+        */
+BEGIN_CRITICAL;
+       test    SSTAT0, SELDO jnz select_out;
+END_CRITICAL;
+       call    start_selection;
+idle_loop_checkbus:
+BEGIN_CRITICAL;
+       test    SSTAT0, SELDO jnz select_out;
+END_CRITICAL;
+       test    SSTAT0, SELDI jnz select_in;
+       test    SCSIPHASE, ~DATA_PHASE_MASK jz idle_loop_check_nonpackreq;
+       test    SCSISIGO, ATNO jz idle_loop_check_nonpackreq;
+       call    unexpected_nonpkt_phase_find_ctxt;
+idle_loop_check_nonpackreq:
+       test    SSTAT2, NONPACKREQ jz idle_loop_scsi;
+       call    unexpected_nonpkt_phase_find_ctxt;
+idle_loop_scsi:
+BEGIN_CRITICAL;
+       test    LQISTAT2, LQIGSAVAIL jz idle_loop_service_fifos;
+       /*
+        * We have received good status for this transaction.  There may
+        * still be data in our FIFOs draining to the host.  Setup
+        * monitoring of the draining process or complete the SCB.
+        */
+good_status_IU_done:
+       bmov    SCBPTR, GSFIFO, 2;
+       clr     SCB_SCSI_STATUS;
+       or      SCB_CONTROL, STATUS_RCVD;
+
+       /*
+        * Since this status did not consume a FIFO, we have to
+        * be a bit more dilligent in how we check for FIFOs pertaining
+        * to this transaction.  There are three states that a FIFO still
+        * transferring data may be in.
+        *
+        * 1) Configured and draining to the host, with a pending CLRCHN.
+        * 2) Configured and draining to the host, no pending CLRCHN.
+        * 3) Pending cfg4data, fifo not empty.
+        *
+        * For case 1, we assume that our DMA post of the completed command
+        * will occur after the FIFO finishes draining due to the higher
+        * priority of data FIFO transfers relative to command channel
+        * transfers.
+        *
+        * Case 2 can be detected by noticing that a longjmp is active for the
+        * FIFO and LONGJMP_SCB matches our SCB.  In this case, we allow
+        * the routine servicing the FIFO to complete the SCB.
+        * 
+        * Case 3 implies either a pending or yet to occur save data
+        * pointers for this same context in the other FIFO.  So, if
+        * we detect case 2, we will properly defer the post of the SCB
+        * and achieve the desired result.  The pending cfg4data will
+        * notice that status has been received and complete the SCB.
+        */
+       test    SCB_SGPTR, SG_LIST_NULL jz good_status_check_fifos;
+       /*
+        * All segments have been loaded (or no data transfer), so
+        * it is safe to complete the command.  Since this was a
+        * cheap command to check for completion, loop to see if
+        * more entries can be removed from the GSFIFO.
+        */
+       call    complete;
+END_CRITICAL;
+       jmp     idle_loop_scsi;
+BEGIN_CRITICAL;
+good_status_check_fifos:
+       clc;
+       bmov    ARG_1, SCBPTR, 2;
+       SET_MODE(M_DFF0, M_DFF0);
+       call    check_fifo;
+       jc      idle_loop_service_fifos;
+       SET_MODE(M_DFF1, M_DFF1);
+       call    check_fifo;
+       jc      idle_loop_service_fifos;
+       SET_MODE(M_SCSI, M_SCSI);
+       call    queue_scb_completion;
+END_CRITICAL;
+idle_loop_service_fifos:
+       SET_MODE(M_DFF0, M_DFF0);
+       test    LONGJMP_ADDR[1], INVALID_ADDR jnz idle_loop_next_fifo;
+       call    longjmp;
+idle_loop_next_fifo:
+       SET_MODE(M_DFF1, M_DFF1);
+       test    LONGJMP_ADDR[1], INVALID_ADDR jnz idle_loop_last_fifo_done;
+       call    longjmp;
+idle_loop_last_fifo_done:
+       call    idle_loop_cchan;
+       jmp     idle_loop;
+
+idle_loop_cchan:
+       SET_MODE(M_CCHAN, M_CCHAN);
+       test    CCSCBCTL, CCARREN|CCSCBEN jz scbdma_idle;
+       test    CCSCBCTL, CCSCBDIR jnz fetch_new_scb_inprog;
+       test    CCSCBCTL, CCSCBDONE jz return;
+       /* FALLTHROUGH */
+scbdma_tohost_done:
+       test    CCSCBCTL, CCARREN jz fill_qoutfifo_dmadone;
+       /*
+        * A complete SCB upload requires no intervention.
+        * The SCB is already on the COMPLETE_SCB list
+        * and its completion notification will now be
+        * handled just like any other SCB.
+        */
+       and     CCSCBCTL, ~(CCARREN|CCSCBEN) ret;
+fill_qoutfifo_dmadone:
+       and     CCSCBCTL, ~(CCARREN|CCSCBEN);
+       mvi     INTSTAT, CMDCMPLT;
+       mvi     COMPLETE_SCB_DMAINPROG_HEAD[1], SCB_LIST_NULL;
+       bmov    QOUTFIFO_NEXT_ADDR, SCBHADDR, 4;
+       test    QOFF_CTLSTA, SDSCB_ROLLOVR jz return;
+       bmov    QOUTFIFO_NEXT_ADDR, SHARED_DATA_ADDR, 4 ret;
+
+fetch_new_scb_inprog:
+       test    CCSCBCTL, ARRDONE jz return;
+fetch_new_scb_done:
+       and     CCSCBCTL, ~(CCARREN|CCSCBEN);
+       bmov    REG0, SCBPTR, 2;
+       /* Update the next SCB address to download. */
+       bmov    NEXT_QUEUED_SCB_ADDR, SCB_NEXT_SCB_BUSADDR, 4;
+       mvi     SCB_NEXT[1], SCB_LIST_NULL;
+       mvi     SCB_NEXT2[1], SCB_LIST_NULL;
+       /*
+        * SCBs that want to send messages are always
+        * queued independently.  This ensures that they
+        * are at the head of the SCB list to select out
+        * to a target and we will see the MK_MESSAGE flag.
+        */
+       test    SCB_CONTROL, MK_MESSAGE jnz first_new_target_scb;
+       shr     SINDEX, 3, SCB_SCSIID;
+       and     SINDEX, ~0x1;
+       mvi     SINDEX[1], (WAITING_SCB_TAILS >> 8);
+       bmov    DINDEX, SINDEX, 2;
+       bmov    SCBPTR, SINDIR, 2;
+       bmov    DINDIR, REG0, 2;
+       cmp     SCBPTR[1], SCB_LIST_NULL je first_new_target_scb;
+       bmov    SCB_NEXT, REG0, 2;
+fetch_new_scb_fini:
+       /* Increment our position in the QINFIFO. */
+       mov     NONE, SNSCB_QOFF ret;
+first_new_target_scb:
+       cmp     WAITING_TID_HEAD[1], SCB_LIST_NULL je first_new_scb;
+       bmov    SCBPTR, WAITING_TID_TAIL, 2;
+       bmov    SCB_NEXT2, REG0, 2;
+       bmov    WAITING_TID_TAIL, REG0, 2;
+       /* Increment our position in the QINFIFO. */
+       mov     NONE, SNSCB_QOFF ret;
+first_new_scb:
+       bmov    WAITING_TID_HEAD, REG0, 2;
+       bmov    WAITING_TID_TAIL, REG0, 2;
+       /* Increment our position in the QINFIFO. */
+       mov     NONE, SNSCB_QOFF ret;
+
+scbdma_idle:
+       /*
+        * Give precedence to downloading new SCBs to execute
+        * unless select-outs are currently frozen.
+        * XXX Use a timer to prevent completion starvation.
+        */
+       test    SEQ_FLAGS2, SELECTOUT_QFROZEN jnz . + 2;
+BEGIN_CRITICAL;
+       test    QOFF_CTLSTA, NEW_SCB_AVAIL jnz fetch_new_scb;
+       cmp     COMPLETE_DMA_SCB_HEAD[1], SCB_LIST_NULL jne dma_complete_scb;
+       cmp     COMPLETE_SCB_HEAD[1], SCB_LIST_NULL je return;
+       /* FALLTHROUGH */
+fill_qoutfifo:
+       /*
+        * Keep track of the SCBs we are dmaing just
+        * in case the DMA fails or is aborted.
+        */
+       bmov    COMPLETE_SCB_DMAINPROG_HEAD, COMPLETE_SCB_HEAD, 2;
+       mvi     CCSCBCTL, CCSCBRESET;
+       bmov    SCBHADDR, QOUTFIFO_NEXT_ADDR, 4;
+       bmov    CCSCBRAM, COMPLETE_SCB_HEAD, 2;
+       bmov    SCBPTR, COMPLETE_SCB_HEAD, 2;
+       jmp     fill_qoutfifo_first_entry;
+fill_qoutfifo_loop:
+       bmov    CCSCBRAM, SCB_NEXT_COMPLETE, 2;
+       bmov    SCBPTR, SCB_NEXT_COMPLETE, 2;
+fill_qoutfifo_first_entry:
+       mov     NONE, SDSCB_QOFF;
+       cmp     SCB_NEXT_COMPLETE[1], SCB_LIST_NULL je fill_qoutfifo_done;
+       cmp     CCSCBADDR, CCSCBADDR_MAX je fill_qoutfifo_done;
+       test    QOFF_CTLSTA, SDSCB_ROLLOVR jz fill_qoutfifo_loop;
+fill_qoutfifo_done:
+       mov     SCBHCNT, CCSCBADDR;
+       mvi     CCSCBCTL, CCSCBEN|CCSCBRESET;
+       bmov    COMPLETE_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
+       mvi     SCB_NEXT_COMPLETE[1], SCB_LIST_NULL ret;
+
+fetch_new_scb:
+       bmov    SCBHADDR, NEXT_QUEUED_SCB_ADDR, 4;
+       mvi     CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET jmp dma_scb;
+dma_complete_scb:
+       bmov    SCBPTR, COMPLETE_DMA_SCB_HEAD, 2;
+       bmov    SCBHADDR, SCB_BUSADDR, 4;
+       mvi     CCARREN|CCSCBEN|CCSCBRESET call dma_scb;
+       /*
+        * Now that we've started the DMA, push us onto
+        * the normal completion queue to have our SCBID
+        * posted to the kernel.
+        */
+       bmov    COMPLETE_DMA_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
+       bmov    SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
+       bmov    COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
+END_CRITICAL;
+
+/*
+ * Either post or fetch an SCB from host memory.  The caller
+ * is responsible for polling for transfer completion.
+ *
+ * Prerequisits: Mode == M_CCHAN
+ *              SINDEX contains CCSCBCTL flags
+ *              SCBHADDR set to Host SCB address
+ *              SCBPTR set to SCB src location on "push" operations
+ */
+SET_SRC_MODE   M_CCHAN;
+SET_DST_MODE   M_CCHAN;
+dma_scb:
+       mvi     SCBHCNT, SCB_TRANSFER_SIZE;
+       mov     CCSCBCTL, SINDEX;
+       or      SEQ_FLAGS2, SCB_DMA ret;
+
+BEGIN_CRITICAL;
+setjmp_setscb:
+       bmov    LONGJMP_SCB, SCBPTR, 2;
+setjmp:
+       bmov    LONGJMP_ADDR, STACK, 2 ret;
+setjmp_inline:
+       bmov    LONGJMP_ADDR, STACK, 2;
+longjmp:
+       bmov    STACK, LONGJMP_ADDR, 2 ret;
+END_CRITICAL;
+
+/************************ Packetized LongJmp Routines *************************/
+/*
+ * Must disable interrupts when setting the mode pointer
+ * register as an interrupt occurring mid update will
+ * fail to store the new mode value for restoration on
+ * an iret.
+ */
+set_mode_work_around:
+       mvi     SEQINTCTL, INTVEC1DSL;
+       mov     MODE_PTR, SINDEX;
+       clr     SEQINTCTL ret;
+
+SET_SRC_MODE   M_SCSI;
+SET_DST_MODE   M_SCSI;
+start_selection:
+BEGIN_CRITICAL;
+       if ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0) {
+               /*
+                * Razor #494
+                * Rev A hardware fails to update LAST/CURR/NEXTSCB
+                * correctly after a packetized selection in several
+                * situations:
+                *
+                * 1) If only one command existed in the the queue, the
+                *    LAST/CURR/NEXTSCB are unchanged.
+                *
+                * 2) In a non QAS, protocol allowed phase change,
+                *    the queue is shifted 1 too far.  LASTSCB is
+                *    the last SCB that was correctly processed.
+                * 
+                * 3) In the QAS case, if the full list of commands
+                *    was successfully sent, NEXTSCB is NULL and neither
+                *    CURRSCB nor LASTSCB can be trusted.  We must
+                *    manually walk the list counting MAXCMDCNT elements
+                *    to find the last SCB that was sent correctly.
+                *
+                * To simplify the workaround for this bug in SELDO
+                * handling, we initialize LASTSCB prior to enabling
+                * selection so we can rely on it even for case #1 above.
+                */
+               bmov    LASTSCB, WAITING_TID_HEAD, 2;
+       }
+       bmov    CURRSCB, WAITING_TID_HEAD, 2;
+       bmov    SCBPTR, WAITING_TID_HEAD, 2;
+       shr     SELOID, 4, SCB_SCSIID;
+       /*
+        * If we want to send a message to the device, ensure
+        * we are selecting with atn irregardless of our packetized
+        * agreement.  Since SPI4 only allows target reset or PPR
+        * messages if this is a packetized connection, the change
+        * to our negotiation table entry for this selection will
+        * be cleared when the message is acted on.
+        */
+       test    SCB_CONTROL, MK_MESSAGE jz . + 3;
+       mov     NEGOADDR, SELOID;
+       or      NEGCONOPTS, ENAUTOATNO;
+       or      SCSISEQ0, ENSELO ret;
+END_CRITICAL;
+
+/*
+ * Allocate a FIFO for a non-packetized transaction.
+ * For some reason unkown to me, both FIFOs must be free before we
+ * can allocate a FIFO for a non-packetized transaction.  This
+ * may be fixed in Rev B.
+ */
+allocate_fifo_loop:
+       /*
+        * Do whatever work is required to free a FIFO.
+        */
+       SET_MODE(M_DFF0, M_DFF0);
+       test    LONGJMP_ADDR[1], INVALID_ADDR jnz . + 2;
+       call    longjmp;
+       SET_MODE(M_DFF1, M_DFF1);
+       test    LONGJMP_ADDR[1], INVALID_ADDR jnz . + 2;
+       call    longjmp;
+       SET_MODE(M_SCSI, M_SCSI);
+allocate_fifo:
+       and     A, FIFO0FREE|FIFO1FREE, DFFSTAT;
+       cmp     A, FIFO0FREE|FIFO1FREE jne allocate_fifo_loop;
+take_fifo:
+       bmov    ARG_1, SCBPTR, 2;
+       or      DFFSTAT, CURRFIFO;
+       SET_MODE(M_DFF1, M_DFF1);
+       bmov    SCBPTR, ARG_1, 2 ret;
+
+/*
+ * We have been reselected as an initiator
+ * or selected as a target.
+ */
+SET_SRC_MODE   M_SCSI;
+SET_DST_MODE   M_SCSI;
+select_in:
+       or      SXFRCTL0, SPIOEN;
+       and     SAVED_SCSIID, SELID_MASK, SELID;
+       and     A, OID, IOWNID;
+       or      SAVED_SCSIID, A;
+       mvi     CLRSINT0, CLRSELDI;
+       jmp     ITloop;
+
+/*
+ * We have successfully selected out.
+ *
+ * Clear SELDO.
+ * Dequeue all SCBs sent from the waiting queue
+ * Requeue all SCBs *not* sent to the tail of the waiting queue
+ * Take Razor #494 into account for above.
+ *
+ * In Packetized Mode:
+ *     Return to the idle loop.  Our interrupt handler will take
+ *     care of any incoming L_Qs.
+ *
+ * In Non-Packetize Mode:
+ *     Continue to our normal state machine.
+ */
+SET_SRC_MODE   M_SCSI;
+SET_DST_MODE   M_SCSI;
+select_out:
+BEGIN_CRITICAL;
+       /* Clear out all SCBs that have been successfully sent. */
+       if ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0) {
+               /*
+                * For packetized, the LQO manager clears ENSELO on
+                * the assertion of SELDO.  If we are non-packetized,
+                * LASTSCB and CURRSCB are acuate.
+                */
+               test    SCSISEQ0, ENSELO jnz use_lastscb;
+
+               /*
+                * The update is correct for LQOSTAT1 errors.  All
+                * but LQOBUSFREE are handled by kernel interrupts.
+                * If we see LQOBUSFREE, return to the idle loop.
+                * Once we are out of the select_out critical section,
+                * the kernel will cleanup the LQOBUSFREE and we will
+                * eventually restart the selection if appropriate.
+                */
+               test    LQOSTAT1, LQOBUSFREE jnz idle_loop;
+
+               /*
+                * On a phase change oustside of packet boundaries,
+                * LASTSCB points to the currently active SCB context
+                * on the bus.
+                */
+               test    LQOSTAT2, LQOPHACHGOUTPKT jnz use_lastscb;
+
+               /*
+                * If the hardware has traversed the whole list, NEXTSCB
+                * will be NULL, CURRSCB and LASTSCB cannot be trusted,
+                * but MAXCMDCNT is accurate.  If we stop part way through
+                * the list or only had one command to issue, NEXTSCB[1] is
+                * not NULL and LASTSCB is the last command to go out.
+                */
+               cmp     NEXTSCB[1], SCB_LIST_NULL jne use_lastscb;
+
+               /*
+                * Brute force walk.
+                */
+               bmov    SCBPTR, WAITING_TID_HEAD, 2;
+               mvi     SEQINTCTL, INTVEC1DSL;
+               mvi     MODE_PTR, MK_MODE(M_CFG, M_CFG);
+               mov     A, MAXCMDCNT;
+               mvi     MODE_PTR, MK_MODE(M_SCSI, M_SCSI);
+               clr     SEQINTCTL;
+find_lastscb_loop:
+               dec     A;
+               test    A, 0xFF jz found_last_sent_scb;
+               bmov    SCBPTR, SCB_NEXT, 2;
+               jmp     find_lastscb_loop;
+use_lastscb:
+               bmov    SCBPTR, LASTSCB, 2;
+found_last_sent_scb:
+               bmov    CURRSCB, SCBPTR, 2;
+curscb_ww_done:
+       } else {
+               /*
+                * Untested - Verify with Rev B.
+                */
+               bmov    SCBPTR, CURRSCB, 2;
+       }
+       /*
+        * Requeue any SCBs not sent, to the tail of the waiting Q.
+        */
+       cmp     SCB_NEXT[1], SCB_LIST_NULL je select_out_list_done;
+
+       /*
+        * We know that neither the per-TID list nor the list of
+        * TIDs is empty.  Use this knowledge to our advantage.
+        */
+       bmov    REG0, SCB_NEXT, 2;
+       bmov    SCBPTR, WAITING_TID_TAIL, 2;
+       bmov    SCB_NEXT2, REG0, 2;
+       bmov    WAITING_TID_TAIL, REG0, 2;
+       jmp     select_out_inc_tid_q;
+
+select_out_list_done:
+       /*
+        * The whole list made it.  Just clear our TID's tail pointer
+        * unless we were queued independently due to our need to
+        * send a message.
+        */
+       test    SCB_CONTROL, MK_MESSAGE jnz select_out_inc_tid_q;
+       shr     DINDEX, 3, SCB_SCSIID;
+/* XXX When we switch to SCB_SELOID, put +1 in addition below. */
+       or      DINDEX, 1;      /* Want only the second byte */
+       mvi     DINDEX[1], ((WAITING_SCB_TAILS) >> 8);
+       mvi     DINDIR, SCB_LIST_NULL;
+select_out_inc_tid_q:
+       bmov    SCBPTR, WAITING_TID_HEAD, 2;
+       bmov    WAITING_TID_HEAD, SCB_NEXT2, 2;
+       cmp     WAITING_TID_HEAD[1], SCB_LIST_NULL jne . + 2;
+       mvi     WAITING_TID_TAIL[1], SCB_LIST_NULL;
+       bmov    SCBPTR, CURRSCB, 2;
+END_CRITICAL;
+
+       mvi     CLRSINT0, CLRSELDO;
+
+       test    LQOSTAT2, LQOPHACHGOUTPKT jnz unexpected_nonpkt_phase;
+       test    LQOSTAT1, LQOPHACHGINPKT jnz unexpected_nonpkt_phase;
+
+       /*
+        * If this is a packetized connection, return to our
+        * idle_loop and let our interrupt handler deal with
+        * any connection setup/teardown issues.  The only
+        * exception is the case of MK_MESSAGE SCBs.  In the
+        * A, the LQO manager transitions to LQOSTOP0 even if
+        * we have selected out with ATN asserted and the target
+        * REQs in a non-packet phase.
+        */
+       if ((ahd->bugs & AHD_LQO_ATNO_BUG) != 0) {
+               test    SCB_CONTROL, MK_MESSAGE jz select_out_no_message;
+               test    SCSISIGO, ATNO jnz select_out_non_packetized;
+select_out_no_message:
+       }
+       test    LQOSTAT2, LQOSTOP0 jnz idle_loop;
+
+select_out_non_packetized:
+       /* Non packetized request. */
+       and     SCSISEQ0, ~ENSELO;
+       mov     SAVED_SCSIID, SCB_SCSIID;
+       mov     SAVED_LUN, SCB_LUN;
+       or      SXFRCTL0, SPIOEN;
+
+       /*
+        * As soon as we get a successful selection, the target
+        * should go into the message out phase since we have ATN
+        * asserted.
+        */
+       mvi     MSG_OUT, MSG_IDENTIFYFLAG;
+       mvi     SEQ_FLAGS, NO_CDB_SENT;
+
+       /*
+        * Main loop for information transfer phases.  Wait for the
+        * target to assert REQ before checking MSG, C/D and I/O for
+        * the bus phase.
+        */
+mesgin_phasemis:
+ITloop:
+       call    phase_lock;
+
+       mov     A, LASTPHASE;
+
+       test    A, ~P_DATAIN_DT jz p_data;
+       cmp     A,P_COMMAND     je p_command;
+       cmp     A,P_MESGOUT     je p_mesgout;
+       cmp     A,P_STATUS      je p_status;
+       cmp     A,P_MESGIN      je p_mesgin;
+
+       mvi     SEQINTCODE, BAD_PHASE;
+       jmp     ITloop;                 /* Try reading the bus again. */
+
+/*
+ * Command phase.  Set up the DMA registers and let 'er rip.
+ */
+p_command:
+SET_SRC_MODE   M_DFF1;
+SET_DST_MODE   M_DFF1;
+       test    MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+               jnz p_command_allocate_fifo;
+       /*
+        * Command retry.  Free our current FIFO and
+        * re-allocate a FIFO so transfer state is
+        * reset.
+        */
+       mvi     DFFSXFRCTL, RSTCHN|CLRSHCNT;
+p_command_allocate_fifo:
+       call    allocate_fifo;
+       add     NONE, -17, SCB_CDB_LEN;
+       jnc     p_command_embedded;
+p_command_from_host:
+       bmov    HADDR[0], SCB_CDB_PTR, 11;
+       mvi     SG_CACHE_PRE, LAST_SEG;
+       mvi     DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN);
+       jmp     p_command_loop;
+p_command_embedded:
+       bmov    SHCNT[0], SCB_CDB_LEN,  1;
+       bmov    SHCNT[1], ALLZEROS, 2;
+       bmov    DFDAT, SCB_CDB_STORE, 16; 
+       mvi     DFCNTRL, SCSIEN;
+p_command_loop:
+       test    DFCNTRL, SCSIEN jnz p_command_loop;
+       /*
+        * DMA Channel automatically disabled.
+        * Don't allow a data phase if the command
+        * was not fully transferred.  Make sure that
+        * we clear the IDENTIFY SEEN flag if a retry
+        * falls short too.
+        */
+       and     SEQ_FLAGS, ~NO_CDB_SENT;
+       test    SSTAT2, SDONE jnz ITloop;
+       or      SEQ_FLAGS, NO_CDB_SENT;
+       jmp     ITloop;
+
+
+/*
+ * Status phase.  Wait for the data byte to appear, then read it
+ * and store it into the SCB.
+ */
+SET_SRC_MODE   M_SCSI;
+SET_DST_MODE   M_SCSI;
+p_status:
+       test    SEQ_FLAGS,NOT_IDENTIFIED jz p_status_okay;
+       mvi     SEQINTCODE, PROTO_VIOLATION;
+       jmp     mesgin_done;
+p_status_okay:
+       mov     SCB_SCSI_STATUS, SCSIDAT;
+       or      SCB_CONTROL, STATUS_RCVD;
+       jmp     ITloop;
+
+/*
+ * Message out phase.  If MSG_OUT is MSG_IDENTIFYFLAG, build a full
+ * indentify message sequence and send it to the target.  The host may
+ * override this behavior by setting the MK_MESSAGE bit in the SCB
+ * control byte.  This will cause us to interrupt the host and allow
+ * it to handle the message phase completely on its own.  If the bit
+ * associated with this target is set, we will also interrupt the host,
+ * thereby allowing it to send a message on the next selection regardless
+ * of the transaction being sent.
+ * 
+ * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
+ * This is done to allow the host to send messages outside of an identify
+ * sequence while protecting the seqencer from testing the MK_MESSAGE bit
+ * on an SCB that might not be for the current nexus. (For example, a
+ * BDR message in responce to a bad reselection would leave us pointed to
+ * an SCB that doesn't have anything to do with the current target).
+ *
+ * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
+ * bus device reset).
+ *
+ * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
+ * in case the target decides to put us in this phase for some strange
+ * reason.
+ */
+p_mesgout_retry:
+       /* Turn on ATN for the retry */
+       mvi     SCSISIGO, ATNO;
+p_mesgout:
+       mov     SINDEX, MSG_OUT;
+       cmp     SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
+       test    SCB_CONTROL,MK_MESSAGE  jnz host_message_loop;
+p_mesgout_identify:
+       or      SINDEX, MSG_IDENTIFYFLAG|DISCENB, SCB_LUN;
+       test    SCB_CONTROL, DISCENB jnz . + 2;
+       and     SINDEX, ~DISCENB;
+/*
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_NONPACKET_TAG as the tag value.
+ */
+p_mesgout_tag:
+       test    SCB_CONTROL,TAG_ENB jz  p_mesgout_onebyte;
+       mov     SCSIDAT, SINDEX;        /* Send the identify message */
+       call    phase_lock;
+       cmp     LASTPHASE, P_MESGOUT    jne p_mesgout_done;
+       and     SCSIDAT,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+       call    phase_lock;
+       cmp     LASTPHASE, P_MESGOUT    jne p_mesgout_done;
+       mov     SCB_NONPACKET_TAG jmp p_mesgout_onebyte;
+/*
+ * Interrupt the driver, and allow it to handle this message
+ * phase and any required retries.
+ */
+p_mesgout_from_host:
+       cmp     SINDEX, HOST_MSG        jne p_mesgout_onebyte;
+       jmp     host_message_loop;
+
+p_mesgout_onebyte:
+       mvi     CLRSINT1, CLRATNO;
+       mov     SCSIDAT, SINDEX;
+
+/*
+ * If the next bus phase after ATN drops is message out, it means
+ * that the target is requesting that the last message(s) be resent.
+ */
+       call    phase_lock;
+       cmp     LASTPHASE, P_MESGOUT    je p_mesgout_retry;
+
+p_mesgout_done:
+       mvi     CLRSINT1,CLRATNO;       /* Be sure to turn ATNO off */
+       mov     LAST_MSG, MSG_OUT;
+       mvi     MSG_OUT, MSG_NOOP;      /* No message left */
+       jmp     ITloop;
+
+/*
+ * Message in phase.  Bytes are read using Automatic PIO mode.
+ */
+p_mesgin:
+       /* read the 1st message byte */
+       mvi     ACCUM           call inb_first;
+
+       test    A,MSG_IDENTIFYFLAG      jnz mesgin_identify;
+       cmp     A,MSG_DISCONNECT        je mesgin_disconnect;
+       cmp     A,MSG_SAVEDATAPOINTER   je mesgin_sdptrs;
+       cmp     ALLZEROS,A              je mesgin_complete;
+       cmp     A,MSG_RESTOREPOINTERS   je mesgin_rdptrs;
+       cmp     A,MSG_IGN_WIDE_RESIDUE  je mesgin_ign_wide_residue;
+       cmp     A,MSG_NOOP              je mesgin_done;
+
+/*
+ * Pushed message loop to allow the kernel to
+ * run it's own message state engine.  To avoid an
+ * extra nop instruction after signaling the kernel,
+ * we perform the phase_lock before checking to see
+ * if we should exit the loop and skip the phase_lock
+ * in the ITloop.  Performing back to back phase_locks
+ * shouldn't hurt, but why do it twice...
+ */
+host_message_loop:
+       call    phase_lock;     /* Benign the first time through. */
+       mvi     SEQINTCODE, HOST_MSG_LOOP;
+       cmp     RETURN_1, EXIT_MSG_LOOP je ITloop;
+       cmp     RETURN_1, CONT_MSG_LOOP_WRITE   jne . + 3;
+       mov     SCSIDAT, RETURN_2;
+       jmp     host_message_loop;
+       /* Must be CONT_MSG_LOOP_READ */
+       mov     NONE, SCSIDAT;  /* ACK Byte */
+       jmp     host_message_loop;
+
+mesgin_ign_wide_residue:
+       shr     NEGOADDR, 4, SAVED_SCSIID;
+       test    NEGCONOPTS, WIDEXFER jz mesgin_reject;
+       /* Pull the residue byte */
+       mvi     REG0    call inb_next;
+       cmp     REG0, 0x01 jne mesgin_reject;
+       test    SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 2;
+       test    DATA_COUNT_ODD, 0x1     jz mesgin_done;
+       mvi     SEQINTCODE, IGN_WIDE_RES;
+       jmp     mesgin_done;
+
+mesgin_reject:
+       mvi     MSG_MESSAGE_REJECT      call mk_mesg;
+mesgin_done:
+       mov     NONE,SCSIDAT;           /*dummy read from latch to ACK*/
+       jmp     ITloop;
+
+#define INDEX_DISC_LIST_SCB(target, lun)                               \
+       mov     SCBPTR, lun;                                            \
+       shr     SCBPTR[1], 3, target
+       
+#define INDEX_DISC_LIST(target, lun)                                   \
+       INDEX_DISC_LIST_SCB(target, lun);                               \
+       and     SINDEX, 0x7, target;                                    \
+       shl     SINDEX, 1;                                              \
+       add     SINDEX, (SCB_DISCONNECTED_LISTS & 0xFF);                \
+       mvi     SINDEX[1], ((SCB_DISCONNECTED_LISTS >> 8) & 0xFF)
+
+mesgin_identify:
+       /*
+        * Determine whether a target is using tagged or non-tagged
+        * transactions by first looking at the transaction stored in
+        * the per-device, disconnected array.  If there is no untagged
+        * transaction for this target this must be an untagged transaction.
+        */
+       shr     SINDEX, 4, SAVED_SCSIID;
+       and     SAVED_LUN, MSG_IDENTIFY_LUNMASK, A;
+       INDEX_DISC_LIST(SINDEX, SAVED_LUN);
+       bmov    DINDEX, SINDEX, 2;
+       bmov    SCBPTR, SINDIR, 2;
+       cmp     SCBPTR[1], SCB_LIST_NULL je snoop_tag;
+       test    SCB_CONTROL, TAG_ENB jnz snoop_tag;
+       /* Untagged.  Setup the SCB. */
+       bmov    REG1, SCB_TAG, 4;       /* Save SCB_TAG and SCB_NEXT */
+       jmp dequeue_first_scb;
+
+/*
+ * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
+ * If we get one, we use the tag returned to find the proper
+ * SCB.  The disconnected list contains any outstanding tagged transactions
+ * where SCB_TAG != SCB_NONPACKET_TAG or SCB_NONPACKET_TAG + 256.
+ * After receiving the tag, look for the SCB at SCB locations tag and
+ * tag + 256.  If those SCBs do not match, traverse the disconnected
+ * list until we find the correct SCB.
+ */
+snoop_tag:
+       if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+               or      SEQ_FLAGS, 0x80;
+       }
+       mov     NONE, SCSIDAT;          /* ACK Identify MSG */
+       call    phase_lock;
+       if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+               or      SEQ_FLAGS, 0x1;
+       }
+       cmp     LASTPHASE, P_MESGIN     jne not_found_ITloop;
+       if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+               or      SEQ_FLAGS, 0x2;
+       }
+       cmp     SCSIBUS, MSG_SIMPLE_Q_TAG jne not_found;
+get_tag:
+       /* Save diconnected list head. */
+       bmov    REG0, SCBPTR, 2;
+       clr     SCBPTR[1];
+       mvi     SCBPTR  call inb_next;  /* tag value */
+       mov     REG1, SCBPTR;
+verify_scb:
+       mov     A, REG1;
+       cmp     SCB_NONPACKET_TAG, A jne verify_other_scb;
+       mov     A, SAVED_SCSIID;
+       cmp     SCB_SCSIID, A jne verify_other_scb;
+       mov     A, SAVED_LUN;
+       cmp     SCB_LUN, A je setup_SCB;
+verify_other_scb:
+       xor     SCBPTR[1], 1;
+       test    SCBPTR[1], 0xFF jnz verify_scb;
+
+search_disc_list:
+       /* Restore disconnected list head. */
+       bmov    SCBPTR, REG0, 2;
+       mvi     REG0[1], SCB_LIST_NULL;
+       mov     A, REG1;
+search_disc_list_loop:
+       cmp     SCB_NONPACKET_TAG, A je dequeue_scb;
+       bmov    REG0, SCBPTR, 2;
+       bmov    SCBPTR, SCB_NEXT, 2;
+       cmp     SCBPTR[1], SCB_LIST_NULL jne search_disc_list_loop;
+       jmp     not_found;
+
+dequeue_scb:
+       bmov    REG1, SCB_TAG, 4;       /* Save SCB_TAG and SCB_NEXT */
+       cmp     REG0[1], SCB_LIST_NULL jne dequeue_intermediate_SCB;
+dequeue_first_scb:
+       shr     SINDEX, 4, SAVED_SCSIID;
+       INDEX_DISC_LIST_SCB(SINDEX, SAVED_LUN);
+       /* Update list head. */
+       bmov    DINDIR, REG2, 2;
+       jmp     dequeue_restore;
+dequeue_intermediate_SCB:
+       bmov    SCBPTR, REG0, 2;
+       bmov    SCB_NEXT, REG2, 2;
+dequeue_restore:
+       bmov    SCBPTR, REG1, 2;
+
+/*
+ * Ensure that the SCB the tag points to is for
+ * an SCB transaction to the reconnecting target.
+ */
+setup_SCB:
+       if ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0) {
+               or      SEQ_FLAGS, 0x10;
+       }
+       test    SCB_CONTROL,DISCONNECTED jz not_found;
+       and     SCB_CONTROL,~DISCONNECTED;
+       clr     SEQ_FLAGS;      /* make note of IDENTIFY */
+       test    SCB_SGPTR, SG_LIST_NULL jnz . + 2;
+       call    allocate_fifo;
+/*     mvi     SEQINTCODE, PRINT_RESIDUALS; */
+       /* See if the host wants to send a message upon reconnection */
+       test    SCB_CONTROL, MK_MESSAGE jz mesgin_done;
+       mvi     HOST_MSG        call mk_mesg;
+       jmp     mesgin_done;
+
+not_found:
+       mvi     SEQINTCODE, NO_MATCH;
+       jmp     mesgin_done;
+
+not_found_ITloop:
+       mvi     SEQINTCODE, NO_MATCH;
+       jmp     ITloop;
+
+/*
+ * We received a "command complete" message.  Put the SCB on the complete
+ * queue and trigger a completion interrupt via the idle loop.  Before doing
+ * so, check to see if there
+ * is a residual or the status byte is something other than STATUS_GOOD (0).
+ * In either of these conditions, we upload the SCB back to the host so it can
+ * process this information.  In the case of a non zero status byte, we 
+ * additionally interrupt the kernel driver synchronously, allowing it to
+ * decide if sense should be retrieved.  If the kernel driver wishes to request
+ * sense, it will fill the kernel SCB with a request sense command, requeue
+ * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting 
+ * RETURN_1 to SEND_SENSE.
+ */
+mesgin_complete:
+
+       /*
+        * If ATN is raised, we still want to give the target a message.
+        * Perhaps there was a parity error on this last message byte.
+        * Either way, the target should take us to message out phase
+        * and then attempt to complete the command again.  We should use a
+        * critical section here to guard against a timeout triggering
+        * for this command and setting ATN while we are still processing
+        * the completion.
+       test    SCSISIGI, ATNI jnz mesgin_done;
+        */
+
+       /*
+        * If the target never sent an identify message but instead went
+        * to mesgin to give an invalid message, let the host abort us.
+        */
+       test    SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT   jz . + 3;
+       mvi     SEQINTCODE, PROTO_VIOLATION;
+       jmp     mesgin_done;
+
+       /*
+        * If the target never gave us status information, have
+        * the host abort the command.
+        */
+       test    SCB_CONTROL, STATUS_RCVD jz . - 2;
+
+       /*
+        * See if we attempted to deliver a message but the target ingnored us.
+        */
+       test    SCB_CONTROL, MK_MESSAGE jz . + 2;
+       mvi     SEQINTCODE, MKMSG_FAILED;
+       call    queue_scb_completion;
+       jmp     await_busfree;
+
+freeze_queue:
+       /* Cancel any pending select-out. */
+       test    SSTAT0, SELDO jnz . + 2;
+       and     SCSISEQ0, ~ENSELO;
+       mov     ACCUM_SAVE, A;
+       clr     A;
+       add     QFREEZE_COUNT, 1;
+       adc     QFREEZE_COUNT[1], A;
+       or      SEQ_FLAGS2, SELECTOUT_QFROZEN;
+       mov     A, ACCUM_SAVE ret;
+
+queue_arg1_scb_completion:
+       SET_MODE(M_SCSI, M_SCSI);
+       bmov    SCBPTR, ARG_1, 2;
+queue_scb_completion:
+       test    SCB_SCSI_STATUS,0xff    jnz bad_status;
+       /*
+        * Check for residuals
+        */
+       test    SCB_SGPTR, SG_LIST_NULL jnz complete;   /* No xfer */
+       test    SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
+       test    SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;
+complete:
+       bmov    SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
+       bmov    COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
+bad_status:
+       cmp     SCB_SCSI_STATUS, STATUS_PKT_SENSE je upload_scb;
+       call    freeze_queue;
+upload_scb:
+       bmov    SCB_NEXT_COMPLETE, COMPLETE_DMA_SCB_HEAD, 2;
+       bmov    COMPLETE_DMA_SCB_HEAD, SCBPTR, 2;
+       or      SCB_SGPTR, SG_STATUS_VALID ret;
+
+/*
+ * Is it a disconnect message?  Set a flag in the SCB to remind us
+ * and await the bus going free.  If this is an untagged transaction
+ * store the SCB id for it in our untagged target table for lookup on
+ * a reselction.
+ */
+mesgin_disconnect:
+       /*
+        * If ATN is raised, we still want to give the target a message.
+        * Perhaps there was a parity error on this last message byte
+        * or we want to abort this command.  Either way, the target
+        * should take us to message out phase and then attempt to
+        * disconnect again.
+        * XXX - Wait for more testing.
+       test    SCSISIGI, ATNI jnz mesgin_done;
+        */
+       test    SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT   jz disconnect_allowed;
+       mvi     SEQINTCODE, PROTO_VIOLATION;
+       jmp     mesgin_done;
+disconnect_allowed:
+       or      SCB_CONTROL,DISCONNECTED;
+       test    SCB_CONTROL, TAG_ENB jz queue_disc_scb;
+       mov     A, SCB_NONPACKET_TAG;
+       cmp     SCBPTR, A je await_busfree;
+queue_disc_scb:
+       bmov    REG0, SCBPTR, 2;
+       shr     SINDEX, 4, SCB_SCSIID;
+       INDEX_DISC_LIST(SINDEX, SCB_LUN);
+       bmov    DINDEX, SINDEX, 2;
+       bmov    REG1, SINDIR, 2;
+       bmov    DINDIR, REG0, 2;
+       bmov    SCBPTR, REG0, 2;
+       bmov    SCB_NEXT, REG1, 2;
+       /* FALLTHROUGH */
+await_busfree:
+       and     SIMODE1, ~ENBUSFREE;
+       mov     NONE, SCSIDAT;          /* Ack the last byte */
+       call    clear_target_state;
+       test    MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+               jnz await_busfree_not_m_dff;
+SET_SRC_MODE   M_DFF1;
+SET_DST_MODE   M_DFF1;
+await_busfree_clrchn:
+       mvi     DFFSXFRCTL, CLRCHN;
+await_busfree_not_m_dff:
+       test    SSTAT1,REQINIT|BUSFREE  jz .;
+       test    SSTAT1, BUSFREE jnz idle_loop;
+       mvi     SEQINTCODE, MISSED_BUSFREE;
+
+
+/*
+ * Save data pointers message:
+ * Copying RAM values back to SCB, for Save Data Pointers message, but
+ * only if we've actually been into a data phase to change them.  This
+ * protects against bogus data in scratch ram and the residual counts
+ * since they are only initialized when we go into data_in or data_out.
+ * Ack the message as soon as possible.  For chips without S/G pipelining,
+ * we can only ack the message after SHADDR has been saved.  On these
+ * chips, SHADDR increments with every bus transaction, even PIO.
+ */
+SET_SRC_MODE   M_DFF1;
+SET_DST_MODE   M_DFF1;
+mesgin_sdptrs:
+       mov     NONE,SCSIDAT;           /*dummy read from latch to ACK*/
+       test    SEQ_FLAGS, DPHASE       jz ITloop;
+       call    save_pointers;
+       jmp     ITloop;
+
+save_pointers:
+       /*
+        * If we are asked to save our position at the end of the
+        * transfer, just mark us at the end rather than perform a
+        * full save.
+        */
+       test    SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz save_pointers_full;
+       or      SCB_SGPTR, SG_LIST_NULL ret;
+
+save_pointers_full:
+       /*
+        * The SCB_DATAPTR becomes the current SHADDR.
+        * All other information comes directly from our residual
+        * state.
+        */
+       bmov    SCB_DATAPTR, SHADDR, 8;
+       bmov    SCB_DATACNT, SCB_RESIDUAL_DATACNT, 8 ret;
+
+/*
+ * Restore pointers message?  Data pointers are recopied from the
+ * SCB anytime we enter a data phase for the first time, so all
+ * we need to do is clear the DPHASE flag and let the data phase
+ * code do the rest.  We also reset/reallocate the FIFO to make
+ * sure we have a clean start for the next data phase.
+ */
+mesgin_rdptrs:
+       and     SEQ_FLAGS, ~DPHASE;
+       test    MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1)) jnz msgin_rdptrs_get_fifo;
+       mvi     DFFSXFRCTL, RSTCHN|CLRSHCNT;
+msgin_rdptrs_get_fifo:
+       call    allocate_fifo;
+       jmp     mesgin_done;
+
+clear_target_state:
+       mvi     LASTPHASE, P_BUSFREE;
+       /* clear target specific flags */
+       mvi     SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT ret;
+
+phase_lock:     
+       test    SCSIPHASE, 0xFF jz .;
+phase_lock_latch_phase:
+       and     LASTPHASE, PHASE_MASK, SCSISIGI ret;
+
+/*
+ * Functions to read data in Automatic PIO mode.
+ *
+ * An ACK is not sent on input from the target until SCSIDATL is read from.
+ * So we wait until SCSIDATL is latched (the usual way), then read the data
+ * byte directly off the bus using SCSIBUSL.  When we have pulled the ATN
+ * line, or we just want to acknowledge the byte, then we do a dummy read
+ * from SCISDATL.  The SCSI spec guarantees that the target will hold the
+ * data byte on the bus until we send our ACK.
+ *
+ * The assumption here is that these are called in a particular sequence,
+ * and that REQ is already set when inb_first is called.  inb_{first,next}
+ * use the same calling convention as inb.
+ */
+inb_next:
+       mov     NONE,SCSIDAT;           /*dummy read from latch to ACK*/
+inb_next_wait:
+       /*
+        * If there is a parity error, wait for the kernel to
+        * see the interrupt and prepare our message response
+        * before continuing.
+        */
+       test    SCSIPHASE, 0xFF jz .;
+inb_next_check_phase:
+       and     LASTPHASE, PHASE_MASK, SCSISIGI;
+       cmp     LASTPHASE, P_MESGIN jne mesgin_phasemis;
+inb_first:
+       clr     DINDEX[1];
+       mov     DINDEX,SINDEX;
+       mov     DINDIR,SCSIBUS  ret;            /*read byte directly from bus*/
+inb_last:
+       mov     NONE,SCSIDAT ret;               /*dummy read from latch to ACK*/
+
+mk_mesg:
+       mvi     SCSISIGO, ATNO;
+       mov     MSG_OUT,SINDEX ret;
+
+SET_SRC_MODE   M_DFF1;
+SET_DST_MODE   M_DFF1;
+disable_ccsgen:
+       test    SG_STATE, FETCH_INPROG jz return;
+       clr     SG_STATE;
+disable_ccsgen_fetch_done:
+       clr     CCSGCTL ret;
+
+toggle_dff_mode:
+       mvi     SEQINTCTL, INTVEC1DSL;
+       xor     MODE_PTR, MK_MODE(M_DFF1, M_DFF1);
+       clr     SEQINTCTL ret;
+
+data_group_idle_loop:
+       mov     SAVED_MODE, MODE_PTR;
+       test    SG_STATE, LOADING_NEEDED jz . + 2;
+       call    service_fifo;
+       call    toggle_dff_mode;
+       test    SG_STATE, LOADING_NEEDED jz . + 2;
+       call    service_fifo;
+       call    idle_loop_cchan;
+       mov     SAVED_MODE jmp set_mode_work_around;
+
+service_fifo:
+       /*
+        * Do we have any prefetch left???
+        */
+       test    SG_STATE, SEGS_AVAIL jnz idle_sg_avail;
+
+       /*
+        * Can this FIFO have access to the S/G cache yet?
+        */
+       test    CCSGCTL, SG_CACHE_AVAIL jz return;
+
+       /* Did we just finish fetching segs? */
+       cmp     CCSGCTL, CCSGEN|SG_CACHE_AVAIL|CCSGDONE
+               je idle_sgfetch_complete;
+
+       /* Are we actively fetching segments? */
+       test    CCSGCTL, CCSGEN jnz return;
+
+       /*
+        * We fetch a "cacheline aligned" and sized amount of data
+        * so we don't end up referencing a non-existant page.
+        * Cacheline aligned is in quotes because the kernel will
+        * set the prefetch amount to a reasonable level if the
+        * cacheline size is unknown.
+        */
+       mvi     SGHCNT, SG_PREFETCH_CNT;
+       and     SGHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR;
+       bmov    SGHADDR[1], SCB_RESIDUAL_SGPTR[1], 3;
+       mvi     CCSGCTL, CCSGEN|SG_CACHE_AVAIL|CCSGRESET;
+       or      SG_STATE, FETCH_INPROG ret;
+idle_sgfetch_complete:
+       /*
+        * Guard against SG_CACHE_AVAIL activating during sg fetch
+        * request in the other FIFO.
+        */
+       test    SG_STATE, FETCH_INPROG jz return;
+       call    disable_ccsgen_fetch_done;
+       and     CCSGADDR, SG_PREFETCH_ADDR_MASK, SCB_RESIDUAL_SGPTR;
+       mvi     SG_STATE, SEGS_AVAIL|LOADING_NEEDED;
+idle_sg_avail:
+       /* Does the hardware have space for another SG entry? */
+       test    DFSTATUS, PRELOAD_AVAIL jz return;
+       if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+               bmov    HADDR, CCSGRAM, 8;
+       } else {
+               bmov    HADDR, CCSGRAM, 4;
+       }
+       bmov    HCNT, CCSGRAM, 3;
+       test    HCNT[0], 0x1 jz . + 2;
+       xor     DATA_COUNT_ODD, 0x1;
+       bmov    SCB_RESIDUAL_DATACNT[3], CCSGRAM, 1;
+       if ((ahd->flags & AHD_39BIT_ADDRESSING) != 0) {
+               and     HADDR[4], SG_HIGH_ADDR_BITS, SCB_RESIDUAL_DATACNT[3];
+       }
+       if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+               /* Skip 4 bytes of pad. */
+               add     CCSGADDR, 4;
+       }
+sg_advance:
+       clr     A;                      /* add sizeof(struct scatter) */
+       add     SCB_RESIDUAL_SGPTR[0],SG_SIZEOF;
+       adc     SCB_RESIDUAL_SGPTR[1],A;
+       adc     SCB_RESIDUAL_SGPTR[2],A;
+       adc     SCB_RESIDUAL_SGPTR[3],A;
+       mov     SINDEX, SCB_RESIDUAL_SGPTR[0];
+       test    DATA_COUNT_ODD, 0x1 jz . + 2;
+       or      SINDEX, ODD_SEG;
+       test    SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 3;
+       or      SINDEX, LAST_SEG;
+       clr     SG_STATE;
+       mov     SG_CACHE_PRE, SINDEX;
+       /*
+        * Load the segment.  Or in HDMAEN here too
+        * just in case HDMAENACK has not come true
+        * by the time this segment is loaded.  If
+        * HDMAENACK is not true, this or will disable
+        * HDMAEN mid-transfer.  We do not want to simply
+        * mvi our original settings as SCSIEN automatically
+        * de-asserts and we don't want to accidentally
+        * re-enable it.
+        */
+       or      DFCNTRL, PRELOADEN|HDMAEN;
+       /*
+        * Do we have another segment in the cache?
+        */
+       add     NONE, SG_PREFETCH_CNT_LIMIT, CCSGADDR;
+       jnc     return;
+       and     SG_STATE, ~SEGS_AVAIL ret;
+
+/*
+ * Initialize the DMA address and counter from the SCB.
+ */
+load_first_seg:
+       bmov    HADDR, SCB_DATAPTR, 11;
+       and     DATA_COUNT_ODD, 0x1, SCB_DATACNT[0];
+       and     REG0, ~SG_FULL_RESID, SCB_SGPTR[0];
+       test    SCB_DATACNT[3], SG_LAST_SEG jz . + 2;
+       or      REG0, LAST_SEG;
+       test    DATA_COUNT_ODD, 0x1 jz . + 2;
+       or      REG0, ODD_SEG;
+       mov     SG_CACHE_PRE, REG0;
+       mvi     DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN);
+       /*
+        * Since we've are entering a data phase, we will
+        * rely on the SCB_RESID* fields.  Initialize the
+        * residual and clear the full residual flag.
+        */
+       and     SCB_SGPTR[0], ~SG_FULL_RESID;
+       bmov    SCB_RESIDUAL_DATACNT[3], SCB_DATACNT[3], 5;
+       /* If we need more S/G elements, tell the idle loop */
+       test    SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jnz . + 2;
+       mvi     SG_STATE, LOADING_NEEDED ret;
+       clr     SG_STATE ret;
+
+p_data:
+       test    SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT   jz p_data_allowed;
+       mvi     SEQINTCODE, PROTO_VIOLATION;
+p_data_allowed:
+       test    SEQ_FLAGS, DPHASE       jz data_phase_initialize;
+
+       /*
+        * If we re-enter the data phase after going through another
+        * phase, our transfer location has almost certainly been
+        * corrupted by the interveining, non-data, transfers.  Ask
+        * the host driver to fix us up based on the transfer residual
+        * unless we already know that we should be bitbucketing.
+        */
+       test    SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jnz p_data_bitbucket;
+       mvi     SEQINTCODE, PDATA_REINIT;
+
+p_data_bitbucket:
+       /*
+        * Turn on `Bit Bucket' mode, wait until the target takes
+        * us to another phase, and then notify the host.
+        */
+       test    MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1))
+               jnz bitbucket_not_m_dff;
+       /*
+        * Ensure that any FIFO contents are cleared out and the
+        * FIFO free'd prior to starting the BITBUCKET.  BITBUCKET
+        * doesn't discard data already in the FIFO.
+        */
+       mvi     DFFSXFRCTL, RSTCHN|CLRSHCNT;
+       SET_MODE(M_SCSI, M_SCSI);
+bitbucket_not_m_dff:
+       or      SXFRCTL1,BITBUCKET;
+       test    SCSIPHASE, DATA_PHASE_MASK jnz .;
+       and     SXFRCTL1, ~BITBUCKET;
+       SET_MODE(M_DFF1, M_DFF1);
+       mvi     SEQINTCODE, DATA_OVERRUN;
+       jmp     ITloop;
+
+data_phase_initialize:
+       test    SCB_SGPTR[0], SG_LIST_NULL jnz p_data_bitbucket;
+       call    load_first_seg;
+data_phase_inbounds:
+       /* We have seen a data phase at least once. */
+       or      SEQ_FLAGS, DPHASE;
+data_group_dma_loop:
+       /*
+        * The transfer is complete if either the last segment
+        * completes or the target changes phase.  Both conditions
+        * will clear SCSIEN.  We test SCSIEN twice during our
+        * "idle loop" to avoid long delays before we notice the
+        * SCSIEN transition.
+        */
+       call    data_group_idle_loop;
+       test    DFCNTRL, SCSIEN jnz data_group_dma_loop;
+
+data_group_dmafinish:
+       /*
+        * The transfer has terminated either due to a phase
+        * change, and/or the completion of the last segment.
+        * We have two goals here.  Do as much other work
+        * as possible while the data fifo drains on a read
+        * and respond as quickly as possible to the standard
+        * messages (save data pointers/disconnect and command
+        * complete) that usually follow a data phase.
+        */
+       call    calc_residual;
+
+       /*
+        * Go ahead and shut down the DMA engine now.
+        */
+       test    DFCNTRL, DIRECTION jnz data_phase_finish;
+data_group_fifoflush:
+       if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+               or      DFCNTRL, FIFOFLUSH;
+       }
+       /*
+        * We have enabled the auto-ack feature.  This means
+        * that the controller may have already transferred
+        * some overrun bytes into the data FIFO and acked them
+        * on the bus.  The only way to detect this situation is
+        * to wait for LAST_SEG_DONE to come true on a completed
+        * transfer and then test to see if the data FIFO is
+        * non-empty.  We know there is more data yet to transfer
+        * if SG_LIST_NULL is not yet set, thus there cannot be
+        * an overrun.
+        */
+       test    SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz data_phase_finish;
+       test    SG_CACHE_SHADOW, LAST_SEG_DONE jz .;
+       test    DFSTATUS, FIFOEMP jnz data_phase_finish;
+       /* Overrun */
+       jmp     p_data;
+data_phase_finish:
+       /*
+        * If the target has left us in data phase, loop through
+        * the dma code again.  We will only loop if there is a
+        * data overrun.  
+        */
+       if ((ahd->flags & AHD_TARGETROLE) != 0) {
+               test    SSTAT0, TARGET jnz data_phase_done;
+       }
+       if ((ahd->flags & AHD_INITIATORROLE) != 0) {
+               test    SSTAT1, REQINIT jz .;
+               test    SCSIPHASE, DATA_PHASE_MASK jnz p_data;
+       }
+
+data_phase_done:
+       /* Kill off any pending prefetch */
+       call    disable_ccsgen;
+
+       if ((ahd->flags & AHD_TARGETROLE) != 0) {
+               test    SEQ_FLAGS, DPHASE_PENDING jz ITloop;
+               /*
+               and     SEQ_FLAGS, ~DPHASE_PENDING;
+                * For data-in phases, wait for any pending acks from the
+                * initiator before changing phase.  We only need to
+                * send Ignore Wide Residue messages for data-in phases.
+               test    DFCNTRL, DIRECTION jz target_ITloop;
+               test    SSTAT1, REQINIT jnz .;
+               test    DATA_COUNT_ODD, 0x1 jz target_ITloop;
+               SET_MODE(M_SCSI, M_SCSI);
+               test    NEGCONOPTS, WIDEXFER jz target_ITloop;
+                */
+               /*
+                * Issue an Ignore Wide Residue Message.
+               mvi     P_MESGIN|BSYO call change_phase;
+               mvi     MSG_IGN_WIDE_RESIDUE call target_outb;
+               mvi     1 call target_outb;
+               jmp     target_ITloop;
+                */
+       } else {
+               jmp     ITloop;
+       }
+
+/*
+ * We assume that, even though data may still be
+ * transferring to the host, that the SCSI side of
+ * the DMA engine is now in a static state.  This
+ * allows us to update our notion of where we are
+ * in this transfer.
+ *
+ * If, by chance, we stopped before being able
+ * to fetch additional segments for this transfer,
+ * yet the last S/G was completely exhausted,
+ * call our idle loop until it is able to load
+ * another segment.  This will allow us to immediately
+ * pickup on the next segment on the next data phase.
+ *
+ * If we happened to stop on the last segment, then
+ * our residual information is still correct from
+ * the idle loop and there is no need to perform
+ * any fixups.
+ */
+calc_residual:
+       test    SG_CACHE_SHADOW, LAST_SEG jz residual_before_last_seg;
+       /* Record if we've consumed all S/G entries */
+       test    MDFFSTAT, SHVALID       jz . + 2;
+       bmov    SCB_RESIDUAL_DATACNT, SHCNT, 3 ret;
+       or      SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL ret;
+residual_before_last_seg:
+       test    MDFFSTAT, SHVALID       jnz sgptr_fixup;
+       /*
+        * Can never happen from an interrupt as the packetized
+        * hardware will only interrupt us once SHVALID or
+        * LAST_SEG_DONE.
+        */
+       call    data_group_idle_loop;
+       jmp     calc_residual;
+
+sgptr_fixup:
+       /*
+        * Fixup the residual next S/G pointer.  The S/G preload
+        * feature of the chip allows us to load two elements
+        * in addition to the currently active element.  We
+        * store the bottom byte of the next S/G pointer in
+        * the SG_CACHE_PTR register so we can restore the
+        * correct value when the DMA completes.  If the next
+        * sg ptr value has advanced to the point where higher
+        * bytes in the address have been affected, fix them
+        * too.
+        */
+       test    SG_CACHE_SHADOW, 0x80 jz sgptr_fixup_done;
+       test    SCB_RESIDUAL_SGPTR[0], 0x80 jnz sgptr_fixup_done;
+       add     SCB_RESIDUAL_SGPTR[1], -1;
+       adc     SCB_RESIDUAL_SGPTR[2], -1; 
+       adc     SCB_RESIDUAL_SGPTR[3], -1;
+sgptr_fixup_done:
+       and     SCB_RESIDUAL_SGPTR[0], SG_ADDR_MASK, SG_CACHE_SHADOW;
+       clr     DATA_COUNT_ODD;
+       test    SG_CACHE_SHADOW, ODD_SEG jz . + 2;
+       or      DATA_COUNT_ODD, 0x1;
+       clr     SCB_RESIDUAL_DATACNT[3]; /* We are not the last seg */
+       bmov    SCB_RESIDUAL_DATACNT, SHCNT, 3 ret;
+
+export seq_isr:
+       nop;    /* Jumps in the first ISR instruction fail on Rev A. */
+       test    SEQINTSRC, SAVEPTRS     jnz saveptr_intr;
+       test    SEQINTSRC, CFG4DATA     jnz cfg4data_intr;
+       test    SEQINTSRC, CFG4ISTAT    jnz cfg4istat_intr;
+       test    SEQINTSRC, CFG4ICMD     jnz cfg4icmd_intr;
+       mvi     SEQINTCODE, INVALID_SEQINT;
+
+/*
+ * There are two types of save pointers interrupts:
+ * The first is a snapshot save pointers where the current FIFO is not
+ * active and contains a snapshot of the current poniter information.
+ * This happens between packets in a stream for a single L_Q.  Since we
+ * are not performing a pointer save, we can safely clear the channel
+ * so it can be used for other transactions.
+ *
+ * The second case is a save pointers on an active FIFO which occurs
+ * if the target changes to a new L_Q or busfrees/QAS' and the transfer
+ * has a residual.  This should occur coincident with a ctxtdone.  We
+ * disable the interrupt and allow our active routine to handle the
+ * save.
+ */
+saveptr_intr:
+       test    DFCNTRL, HDMAENACK jz snapshot_saveptr;
+       and     SEQIMODE, ~ENSAVEPTRS;
+       or      SEQINTCTL, IRET ret;
+snapshot_saveptr:
+       mvi     DFFSXFRCTL, CLRCHN;
+       or      SEQINTCTL, IRET ret;
+
+cfg4data_intr:
+       test    SCB_SGPTR[0], SG_LIST_NULL jnz pkt_handle_overrun;
+       call    load_first_seg;
+       call    pkt_handle_xfer;
+       or      SEQINTCTL, IRET ret;
+
+cfg4istat_intr:
+       call    freeze_queue;
+       add     NONE, -13, SCB_CDB_LEN;
+       jnc     cfg4istat_have_sense_addr;
+       test    SCB_CDB_LEN, SCB_CDB_LEN_PTR jnz cfg4istat_have_sense_addr;
+       /*
+        * Host sets up address/count and enables transfer.
+        */
+       mvi     SEQINTCODE, CFG4ISTAT_INTR;
+       jmp     cfg4istat_setup_handler;
+cfg4istat_have_sense_addr:
+       bmov    HADDR, SCB_SENSE_BUSADDR, 4;
+       mvi     HCNT[1], (AHD_SENSE_BUFSIZE >> 8);
+       mvi     SG_CACHE_PRE, LAST_SEG;
+       mvi     DFCNTRL, PRELOADEN|SCSIEN|HDMAEN;
+cfg4istat_setup_handler:
+       /*
+        * Status pkt is transferring to host.
+        * Wait in idle loop for transfer to complete.
+        */
+       call    pkt_handle_status;
+       or      SEQINTCTL, IRET ret;
+
+/*
+ * See if the target has gone on in this context creating an
+ * overrun condition.  For the write case, the hardware cannot
+ * ack bytes until data is provided.  So, if the target begins
+ * another  packet without changing contexts, implying we are
+ * not sitting on a packet boundary, we are in an overrun
+ * situation.  For the read case, the hardware will continue to
+ * ack bytes into the FIFO, and may even ack the last overrun packet
+ * into the FIFO.   If the FIFO should become non-empty, we are in
+ * a read overrun case.
+ */
+#define check_overrun                                                  \
+       /* Not on a packet boundary. */                                 \
+       test    MDFFSTAT, DLZERO jz pkt_handle_overrun;                 \
+       test    DFSTATUS, FIFOEMP jz pkt_handle_overrun
+
+pkt_handle_xfer:
+       bmov    LONGJMP_SCB, SCBPTR, 2;
+       test    SG_STATE, LOADING_NEEDED jz pkt_last_seg;
+       call    setjmp;
+       test    SEQINTSRC, SAVEPTRS jnz pkt_saveptrs;
+       test    SCSIPHASE, ~DATA_PHASE_MASK jz . + 2;
+       test    SCSISIGO, ATNO jnz . + 2;
+       test    SSTAT2, NONPACKREQ jz pkt_service_fifo;
+       /*
+        * Defer handling of this NONPACKREQ until we
+        * can be sure it pertains to this FIFO.  SAVEPTRS
+        * will not be asserted if the NONPACKREQ is for us,
+        * so we must simulate it if shaddow is valid.  If
+        * shaddow is not valid, keep running this FIFO until we
+        * have satisfied the transfer by loading segments and
+        * waiting for either shaddow valid or last_seg_done.
+        */
+       test    MDFFSTAT, SHVALID jnz pkt_saveptrs;
+pkt_service_fifo:
+       test    SG_STATE, LOADING_NEEDED jnz service_fifo;
+pkt_last_seg:
+       call    setjmp;
+       test    SEQINTSRC, SAVEPTRS jnz pkt_saveptrs;
+       test    SG_CACHE_SHADOW, LAST_SEG_DONE jnz last_pkt_done;
+       test    SCSIPHASE, ~DATA_PHASE_MASK jz . + 2;
+       test    SCSISIGO, ATNO jnz . + 2;
+       test    SSTAT2, NONPACKREQ jz return;
+       test    MDFFSTAT, SHVALID jnz pkt_saveptrs;
+       jmp     return;
+last_pkt_done:
+BEGIN_CRITICAL;
+       if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+               or      DFCNTRL, FIFOFLUSH;
+       }
+       test    SCB_CONTROL, STATUS_RCVD jz wait_pkt_end;
+       check_overrun;
+       or      SCB_SGPTR, SG_LIST_NULL;
+       /*
+        * I think it is safe to skip the FIFO check.
+        * in this case as LAST_SEG_DONE implies
+        * the other FIFO, if ever active for this transfer,
+        * has completed.
+        */
+last_pkt_queue_scb:
+       or      LONGJMP_ADDR[1], INVALID_ADDR;
+       bmov    ARG_1, SCBPTR, 2;
+       mvi     DFFSXFRCTL, CLRCHN;
+       jmp     queue_arg1_scb_completion;
+
+last_pkt_complete:
+       bmov    ARG_1, SCBPTR, 2;
+       mvi     DFFSXFRCTL, CLRCHN;
+check_other_fifo:
+       clc;
+       call    toggle_dff_mode;
+       call    check_fifo;
+       jnc     queue_arg1_scb_completion;
+return:
+       ret;
+
+wait_pkt_end:
+       call    setjmp;
+END_CRITICAL;
+wait_pkt_end_loop:
+       test    SEQINTSRC, CTXTDONE jnz pkt_end;
+       check_overrun;
+       test    SSTAT2, NONPACKREQ jz return;
+       test    SEQINTSRC, CTXTDONE jz unexpected_nonpkt_phase;
+pkt_end:
+BEGIN_CRITICAL;
+       check_overrun;
+       or      LONGJMP_ADDR[1], INVALID_ADDR;
+       or      SCB_SGPTR, SG_LIST_NULL;
+       test    SCB_CONTROL, STATUS_RCVD jnz last_pkt_complete;
+       mvi     DFFSXFRCTL, CLRCHN ret;
+END_CRITICAL;
+
+/*
+ * Either a SAVEPTRS interrupt condition is pending for this FIFO
+ * or we have a pending nonpackreq for this FIFO.  We differentiate
+ * between the two by capturing the state of the SAVEPTRS interrupt
+ * prior to clearing and handling the common code of these two cases.
+ */
+pkt_saveptrs:
+BEGIN_CRITICAL;
+       if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+               or      DFCNTRL, FIFOFLUSH;
+       }
+       mov     REG0, SEQINTSRC;
+       mvi     CLRSEQINTSRC, CLRSAVEPTRS;
+       call    calc_residual;
+       call    save_pointers;
+       call    disable_ccsgen;
+       or      SEQIMODE, ENSAVEPTRS;
+       or      LONGJMP_ADDR[1], INVALID_ADDR;
+pkt_saveptrs_check_status:
+       test    REG0, SAVEPTRS jz unexpected_nonpkt_phase;
+       test    SCB_CONTROL, STATUS_RCVD jz pkt_saveptrs_clrchn;
+       jmp     last_pkt_complete;
+pkt_saveptrs_clrchn:
+       mvi     DFFSXFRCTL, CLRCHN ret;
+END_CRITICAL;
+
+check_status_overrun:
+       test    SHCNT[2], 0xFF jz status_IU_done;
+       mvi     SEQINTCODE, STATUS_OVERRUN;
+       jmp     status_IU_done;
+pkt_handle_status:
+       call    setjmp_setscb;
+       test    MDFFSTAT, LASTSDONE jnz check_status_overrun;
+       test    SEQINTSRC, CTXTDONE jz return;
+status_IU_done:
+BEGIN_CRITICAL;
+       if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) {
+               or      DFCNTRL, FIFOFLUSH;
+       }
+       or      LONGJMP_ADDR[1], INVALID_ADDR;
+       mvi     SCB_SCSI_STATUS, STATUS_PKT_SENSE;
+       or      SCB_CONTROL, STATUS_RCVD;
+       jmp     last_pkt_complete;
+END_CRITICAL;
+
+SET_SRC_MODE   M_DFF0;
+SET_DST_MODE   M_DFF0;
+BEGIN_CRITICAL;
+check_fifo:
+       test    LONGJMP_ADDR[1], INVALID_ADDR jnz return;
+       mov     A, ARG_2;
+       cmp     LONGJMP_SCB[1], A       jne return;
+       mov     A, ARG_1;
+       cmp     LONGJMP_SCB[0], A       jne return;
+       stc     ret;
+END_CRITICAL;
+
+/*
+ * Nonpackreq is a polled status.  It can come true in three situations:
+ * we have received an L_Q, we have sent one or more L_Qs, or there is no
+ * L_Q context associated with this REQ (REQ occurs immediately after a
+ * (re)selection).  Routines that know that the context responsible for this
+ * nonpackreq call directly into unexpected_nonpkt_phase.  In the case of the
+ * top level idle loop, we exhaust all active contexts prior to determining that
+ * we simply do not have the full I_T_L_Q for this phase.
+ */
+unexpected_nonpkt_phase_find_ctxt:
+       /*
+        * This nonpackreq is most likely associated with one of the tags
+        * in a FIFO or an outgoing LQ.  Only treat it as an I_T only
+        * nonpackreq if we've cleared out the FIFOs and handled any
+        * pending SELDO.
+        */
+SET_SRC_MODE   M_SCSI;
+SET_DST_MODE   M_SCSI;
+       and     A, FIFO1FREE|FIFO0FREE, DFFSTAT;
+       cmp     A, FIFO1FREE|FIFO0FREE jne return;
+       test    SSTAT0, SELDO jnz return;
+       mvi     SCBPTR[1], SCB_LIST_NULL;
+unexpected_nonpkt_phase:
+       test    MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1)) jnz . + 3;
+SET_SRC_MODE   M_DFF0;
+SET_DST_MODE   M_DFF0;
+       or      LONGJMP_ADDR[1], INVALID_ADDR;
+       mvi     DFFSXFRCTL, CLRCHN;
+       mvi     CLRSINT2, CLRNONPACKREQ;
+       test    SCSIPHASE, ~(MSG_IN_PHASE|MSG_OUT_PHASE) jnz illegal_phase;
+       mvi     SEQINTCODE, ENTERING_NONPACK;
+       jmp     ITloop;
+
+illegal_phase:
+       mvi     SEQINTCODE, ILLEGAL_PHASE;
+       jmp     ITloop;
+
+/*
+ * We have entered an overrun situation.  If we have working
+ * BITBUCKET, flip that on and let the hardware eat any overrun
+ * data.  Otherwise use an overrun buffer in the host to simulate
+ * BITBUCKET.
+ */
+pkt_handle_overrun:
+       mvi     SEQINTCODE, CFG4OVERRUN;
+       call    freeze_queue;
+       if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) == 0) {
+               SET_MODE(M_SCSI, M_SCSI);
+               or      SXFRCTL1,BITBUCKET;
+SET_SRC_MODE   M_DFF1;
+SET_DST_MODE   M_DFF1;
+       } else {
+               call    load_overrun_buf;
+               mvi     DFCNTRL, (HDMAEN|SCSIEN|PRELOADEN);
+       }
+       call    setjmp;
+       if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0) {
+               test    DFSTATUS, PKT_PRELOAD_AVAIL jz overrun_load_done;
+               call    load_overrun_buf;
+               or      DFCNTRL, PRELOADEN;
+overrun_load_done:
+       }
+       test    SEQINTSRC, CTXTDONE jnz pkt_overrun_end;
+       test    SSTAT2, NONPACKREQ jz return;
+pkt_overrun_end:
+       or      SCB_RESIDUAL_SGPTR, SG_OVERRUN_RESID;
+       test    SEQINTSRC, CTXTDONE jz unexpected_nonpkt_phase;
+       test    SCB_CONTROL, STATUS_RCVD jnz last_pkt_queue_scb;
+       mvi     DFFSXFRCTL, CLRCHN ret;
+
+if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0) {
+load_overrun_buf:
+       /*
+        * Load a dummy segment if preload space is available.
+        */
+       mov     HADDR[0], SHARED_DATA_ADDR;
+       add     HADDR[1], PKT_OVERRUN_BUFOFFSET, SHARED_DATA_ADDR[1];
+       mov     ACCUM_SAVE, A;
+       clr     A;
+       adc     HADDR[2], A, SHARED_DATA_ADDR[2];
+       adc     HADDR[3], A, SHARED_DATA_ADDR[3];
+       mov     A, ACCUM_SAVE;
+       bmov    HADDR[4], ALLZEROS, 4;
+       /* PKT_OVERRUN_BUFSIZE is a multiple of 256 */
+       clr     HCNT[0];
+       mvi     HCNT[1], ((PKT_OVERRUN_BUFSIZE >> 8) & 0xFF);
+       clr     HCNT[2];
+}
+
+cfg4icmd_intr:
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_core.c b/xen/drivers/scsi/aic7xxx/aic79xx_core.c
new file mode 100644 (file)
index 0000000..660af5f
--- /dev/null
@@ -0,0 +1,8344 @@
+/*
+ * Core routines and tables shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#79 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+#include "aicasm/aicasm_insformat.h"
+#else
+#include <dev/aic7xxx/aic79xx_osm.h>
+#include <dev/aic7xxx/aic79xx_inline.h>
+#include <dev/aic7xxx/aicasm/aicasm_insformat.h>
+#endif
+
+/****************************** Softc Data ************************************/
+struct ahd_softc_tailq ahd_tailq = TAILQ_HEAD_INITIALIZER(ahd_tailq);
+
+/***************************** Lookup Tables **********************************/
+char *ahd_chip_names[] =
+{
+       "NONE",
+       "aic7901",
+       "aic7902"
+};
+static const u_int num_chip_names = NUM_ELEMENTS(ahd_chip_names);
+
+/*
+ * Hardware error codes.
+ */
+struct ahd_hard_error_entry {
+        uint8_t errno;
+       char *errmesg;
+};
+
+static struct ahd_hard_error_entry ahd_hard_errors[] = {
+       { DSCTMOUT,     "Discard Timer has timed out" },
+       { ILLOPCODE,    "Illegal Opcode in sequencer program" },
+       { SQPARERR,     "Sequencer Parity Error" },
+       { DPARERR,      "Data-path Parity Error" },
+       { MPARERR,      "Scratch or SCB Memory Parity Error" },
+       { CIOPARERR,    "CIOBUS Parity Error" },
+};
+static const u_int num_errors = NUM_ELEMENTS(ahd_hard_errors);
+
+static struct ahd_phase_table_entry ahd_phase_table[] =
+{
+       { P_DATAOUT,    MSG_NOOP,               "in Data-out phase"     },
+       { P_DATAIN,     MSG_INITIATOR_DET_ERR,  "in Data-in phase"      },
+       { P_DATAOUT_DT, MSG_NOOP,               "in DT Data-out phase"  },
+       { P_DATAIN_DT,  MSG_INITIATOR_DET_ERR,  "in DT Data-in phase"   },
+       { P_COMMAND,    MSG_NOOP,               "in Command phase"      },
+       { P_MESGOUT,    MSG_NOOP,               "in Message-out phase"  },
+       { P_STATUS,     MSG_INITIATOR_DET_ERR,  "in Status phase"       },
+       { P_MESGIN,     MSG_PARITY_ERROR,       "in Message-in phase"   },
+       { P_BUSFREE,    MSG_NOOP,               "while idle"            },
+       { 0,            MSG_NOOP,               "in unknown phase"      }
+};
+
+/*
+ * In most cases we only wish to itterate over real phases, so
+ * exclude the last element from the count.
+ */
+static const u_int num_phases = NUM_ELEMENTS(ahd_phase_table) - 1;
+
+/* Our Sequencer Program */
+#include "aic79xx_seq.h"
+
+/**************************** Function Declarations ***************************/
+static void            ahd_handle_transmission_error(struct ahd_softc *ahd);
+static void            ahd_handle_lqiphase_error(struct ahd_softc *ahd,
+                                                 u_int lqistat1);
+static int             ahd_handle_pkt_busfree(struct ahd_softc *ahd,
+                                              u_int busfreetime);
+static int             ahd_handle_nonpkt_busfree(struct ahd_softc *ahd);
+static void            ahd_force_renegotiation(struct ahd_softc *ahd,
+                                               struct ahd_devinfo *devinfo);
+
+static struct ahd_tmode_tstate*
+                       ahd_alloc_tstate(struct ahd_softc *ahd,
+                                        u_int scsi_id, char channel);
+#ifdef AHD_TARGET_MODE
+static void            ahd_free_tstate(struct ahd_softc *ahd,
+                                       u_int scsi_id, char channel, int force);
+#endif
+static void            ahd_devlimited_syncrate(struct ahd_softc *ahd,
+                                               struct ahd_initiator_tinfo *,
+                                               u_int *period,
+                                               u_int *ppr_options,
+                                               role_t role);
+static void            ahd_update_neg_table(struct ahd_softc *ahd,
+                                            struct ahd_devinfo *devinfo,
+                                            struct ahd_transinfo *tinfo);
+static void            ahd_update_pending_scbs(struct ahd_softc *ahd);
+static void            ahd_fetch_devinfo(struct ahd_softc *ahd,
+                                         struct ahd_devinfo *devinfo);
+static void            ahd_scb_devinfo(struct ahd_softc *ahd,
+                                       struct ahd_devinfo *devinfo,
+                                       struct scb *scb);
+static void            ahd_setup_initiator_msgout(struct ahd_softc *ahd,
+                                                  struct ahd_devinfo *devinfo,
+                                                  struct scb *scb);
+static void            ahd_build_transfer_msg(struct ahd_softc *ahd,
+                                              struct ahd_devinfo *devinfo);
+static void            ahd_construct_sdtr(struct ahd_softc *ahd,
+                                          struct ahd_devinfo *devinfo,
+                                          u_int period, u_int offset);
+static void            ahd_construct_wdtr(struct ahd_softc *ahd,
+                                          struct ahd_devinfo *devinfo,
+                                          u_int bus_width);
+static void            ahd_construct_ppr(struct ahd_softc *ahd,
+                                         struct ahd_devinfo *devinfo,
+                                         u_int period, u_int offset,
+                                         u_int bus_width, u_int ppr_options);
+static void            ahd_clear_msg_state(struct ahd_softc *ahd);
+static void            ahd_handle_message_phase(struct ahd_softc *ahd);
+typedef enum {
+       AHDMSG_1B,
+       AHDMSG_2B,
+       AHDMSG_EXT
+} ahd_msgtype;
+static int             ahd_sent_msg(struct ahd_softc *ahd, ahd_msgtype type,
+                                    u_int msgval, int full);
+static int             ahd_parse_msg(struct ahd_softc *ahd,
+                                     struct ahd_devinfo *devinfo);
+static int             ahd_handle_msg_reject(struct ahd_softc *ahd,
+                                             struct ahd_devinfo *devinfo);
+static void            ahd_handle_ign_wide_residue(struct ahd_softc *ahd,
+                                               struct ahd_devinfo *devinfo);
+static void            ahd_reinitialize_dataptrs(struct ahd_softc *ahd);
+static void            ahd_handle_devreset(struct ahd_softc *ahd,
+                                           struct ahd_devinfo *devinfo,
+                                           cam_status status, char *message,
+                                           int verbose_level);
+#if AHD_TARGET_MODE
+static void            ahd_setup_target_msgin(struct ahd_softc *ahd,
+                                              struct ahd_devinfo *devinfo,
+                                              struct scb *scb);
+#endif
+
+static bus_size_t      ahd_sglist_size(struct ahd_softc *ahd);
+static bus_size_t      ahd_sglist_allocsize(struct ahd_softc *ahd);
+static bus_dmamap_callback_t
+                       ahd_dmamap_cb; 
+static void            ahd_initialize_hscbs(struct ahd_softc *ahd);
+static int             ahd_init_scbdata(struct ahd_softc *ahd);
+static void            ahd_fini_scbdata(struct ahd_softc *ahd);
+static void            ahd_setup_iocell_workaround(struct ahd_softc *ahd);
+static void            ahd_iocell_first_selection(struct ahd_softc *ahd);
+static void            ahd_chip_init(struct ahd_softc *ahd);
+static void            ahd_qinfifo_requeue(struct ahd_softc *ahd,
+                                           struct scb *prev_scb,
+                                           struct scb *scb);
+static int             ahd_qinfifo_count(struct ahd_softc *ahd);
+static int             ahd_search_scb_list(struct ahd_softc *ahd, int target,
+                                           char channel, int lun, u_int tag,
+                                           role_t role, uint32_t status,
+                                           ahd_search_action action,
+                                           u_int *list_head, u_int tid);
+static void            ahd_stitch_tid_list(struct ahd_softc *ahd,
+                                           u_int tid_prev, u_int tid_cur,
+                                           u_int tid_next);
+static void            ahd_add_scb_to_free_list(struct ahd_softc *ahd,
+                                                u_int scbid);
+static u_int           ahd_rem_wscb(struct ahd_softc *ahd, u_int scbid,
+                                    u_int prev, u_int next, u_int tid);
+static void            ahd_reset_current_bus(struct ahd_softc *ahd);
+static ahd_callback_t  ahd_reset_poll;
+#ifdef AHD_DUMP_SEQ
+static void            ahd_dumpseq(struct ahd_softc *ahd);
+#endif
+static void            ahd_loadseq(struct ahd_softc *ahd);
+static int             ahd_check_patch(struct ahd_softc *ahd,
+                                       struct patch **start_patch,
+                                       u_int start_instr, u_int *skip_addr);
+static u_int           ahd_resolve_seqaddr(struct ahd_softc *ahd,
+                                           u_int address);
+static void            ahd_download_instr(struct ahd_softc *ahd,
+                                          u_int instrptr, uint8_t *dconsts);
+#ifdef AHD_TARGET_MODE
+static void            ahd_queue_lstate_event(struct ahd_softc *ahd,
+                                              struct ahd_tmode_lstate *lstate,
+                                              u_int initiator_id,
+                                              u_int event_type,
+                                              u_int event_arg);
+static void            ahd_update_scsiid(struct ahd_softc *ahd,
+                                         u_int targid_mask);
+static int             ahd_handle_target_cmd(struct ahd_softc *ahd,
+                                             struct target_cmd *cmd);
+#endif
+
+/******************************** Private Inlines *****************************/
+static __inline void   ahd_assert_atn(struct ahd_softc *ahd);
+static __inline int    ahd_currently_packetized(struct ahd_softc *ahd);
+static __inline int    ahd_set_active_fifo(struct ahd_softc *ahd);
+
+static __inline void
+ahd_assert_atn(struct ahd_softc *ahd)
+{
+       ahd_outb(ahd, SCSISIGO, ATNO);
+}
+
+/*
+ * Determine if the current connection has a packetized
+ * agreement.  This does not necessarily mean that we
+ * are currently in a packetized transfer.  We could
+ * just as easily be sending or receiving a message.
+ */
+static __inline int
+ahd_currently_packetized(struct ahd_softc *ahd)
+{
+       ahd_mode_state   saved_modes;
+       int              packetized;
+
+       saved_modes = ahd_save_modes(ahd);
+       if ((ahd->bugs & AHD_PKTIZED_STATUS_BUG) != 0) {
+               /*
+                * The packetized bit refers to the last
+                * connection, not the current one.  Check
+                * for non-zero LQISTATE instead.
+                */
+               ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+               packetized = ahd_inb(ahd, LQISTATE) != 0;
+       } else {
+               ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+               packetized = ahd_inb(ahd, LQISTAT2) & PACKETIZED;
+       }
+       ahd_restore_modes(ahd, saved_modes);
+       return (packetized);
+}
+
+static __inline int
+ahd_set_active_fifo(struct ahd_softc *ahd)
+{
+       u_int active_fifo;
+
+       AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+       active_fifo = ahd_inb(ahd, DFFSTAT) & CURRFIFO;
+/* XXX This is a three possition switch in the B. */
+       switch (active_fifo) {
+       case 0:
+       case 1:
+               ahd_set_modes(ahd, active_fifo, active_fifo);
+               return (1);
+       default:
+               return (0);
+       }
+}
+
+/************************* Sequencer Execution Control ************************/
+/*
+ * Restart the sequencer program from address zero
+ */
+void
+ahd_restart(struct ahd_softc *ahd)
+{
+
+       ahd_pause(ahd);
+
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+       /* No more pending messages */
+       ahd_clear_msg_state(ahd);
+       ahd_outb(ahd, SCSISIGO, 0);             /* De-assert BSY */
+       ahd_outb(ahd, MSG_OUT, MSG_NOOP);       /* No message to send */
+       ahd_outb(ahd, SXFRCTL1, ahd_inb(ahd, SXFRCTL1) & ~BITBUCKET);
+       ahd_outb(ahd, SEQINTCTL, 0);
+       ahd_outb(ahd, LASTPHASE, P_BUSFREE);
+       ahd_outb(ahd, SEQ_FLAGS, 0);
+       ahd_outb(ahd, SAVED_SCSIID, 0xFF);
+       ahd_outb(ahd, SAVED_LUN, 0xFF);
+
+       /*
+        * Ensure that the sequencer's idea of TQINPOS
+        * matches our own.  The sequencer increments TQINPOS
+        * only after it sees a DMA complete and a reset could
+        * occur before the increment leaving the kernel to believe
+        * the command arrived but the sequencer to not.
+        */
+       ahd_outb(ahd, TQINPOS, ahd->tqinfifonext);
+
+       /* Always allow reselection */
+       ahd_outb(ahd, SCSISEQ1,
+                ahd_inb(ahd, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
+       /* Ensure that no DMA operations are in progress */
+       ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+       ahd_outb(ahd, SCBHCNT, 0);
+       ahd_outb(ahd, CCSCBCTL, CCSCBRESET);
+
+       ahd_outb(ahd, SEQCTL0, FASTMODE|SEQRESET);
+       ahd_unpause(ahd);
+}
+
+void
+ahd_clear_fifo(struct ahd_softc *ahd, u_int fifo)
+{
+       ahd_mode_state   saved_modes;
+
+#if AHD_DEBUG
+       if ((ahd_debug & AHD_SHOW_FIFOS) != 0)
+               printf("%s: Clearing FIFO %d\n", ahd_name(ahd), fifo);
+#endif
+       saved_modes = ahd_save_modes(ahd);
+       ahd_set_modes(ahd, fifo, fifo);
+       ahd_outb(ahd, DFFSXFRCTL, RSTCHN|CLRSHCNT);
+       if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0)
+               ahd_outb(ahd, CCSGCTL, CCSGRESET);
+       ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR);
+       ahd_outb(ahd, SG_STATE, 0);
+       ahd_restore_modes(ahd, saved_modes);
+}
+
+/************************* Input/Output Queues ********************************/
+void
+ahd_run_qoutfifo(struct ahd_softc *ahd)
+{
+       struct scb *scb;
+       u_int  scb_index;
+
+       ahd_sync_qoutfifo(ahd, BUS_DMASYNC_POSTREAD);
+       while (ahd->qoutfifo[ahd->qoutfifonext] != SCB_LIST_NULL_LE) {
+
+               scb_index = ahd_le16toh(ahd->qoutfifo[ahd->qoutfifonext]);
+               scb = ahd_lookup_scb(ahd, scb_index);
+               if (scb == NULL) {
+                       printf("%s: WARNING no command for scb %d "
+                              "(cmdcmplt)\nQOUTPOS = %d\n",
+                              ahd_name(ahd), scb_index,
+                              ahd->qoutfifonext);
+                       ahd_dump_card_state(ahd);
+                       ahd->qoutfifonext = AHD_QOUT_WRAP(ahd->qoutfifonext+1);
+                       continue;
+               }
+
+               if ((ahd->qoutfifonext & 0x01) == 0x01) {
+
+                       /*
+                        * Clear 32bits of QOUTFIFO at a time
+                        * so that we don't clobber an incoming
+                        * 16bit DMA to the array on architectures
+                        * that only support 32bit load and store
+                        * operations.
+                        */
+                       ahd->qoutfifo[ahd->qoutfifonext - 1] = SCB_LIST_NULL_LE;
+                       ahd->qoutfifo[ahd->qoutfifonext] = SCB_LIST_NULL_LE;
+                       ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
+                                       ahd->shared_data_dmamap,
+                                       /*offset*/(ahd->qoutfifonext - 1)*2,
+                                       /*len*/4, BUS_DMASYNC_PREREAD);
+               }
+               ahd->qoutfifonext = AHD_QOUT_WRAP(ahd->qoutfifonext+1);
+
+               ahd_complete_scb(ahd, scb);
+       }
+}
+
+void
+ahd_run_untagged_queues(struct ahd_softc *ahd)
+{
+       int i;
+
+       for (i = 0; i < 16; i++)
+               ahd_run_untagged_queue(ahd, &ahd->untagged_queues[i]);
+}
+
+void
+ahd_run_untagged_queue(struct ahd_softc *ahd, struct scb_tailq *queue)
+{
+       struct scb *scb;
+
+       if (ahd->untagged_queue_lock != 0)
+               return;
+
+       if ((scb = TAILQ_FIRST(queue)) != NULL
+        && (scb->flags & SCB_ACTIVE) == 0) {
+               scb->flags |= SCB_ACTIVE;
+               ahd_queue_scb(ahd, scb);
+       }
+}
+
+/************************* Interrupt Handling *********************************/
+void
+ahd_handle_hwerrint(struct ahd_softc *ahd)
+{
+       /*
+        * Some catastrophic hardware error has occurred.
+        * Print it for the user and disable the controller.
+        */
+       int i;
+       int error;
+
+       error = ahd_inb(ahd, ERROR);
+       for (i = 0; i < num_errors; i++) {
+               if ((error & ahd_hard_errors[i].errno) != 0)
+                       printf("%s: hwerrint, %s\n",
+                              ahd_name(ahd), ahd_hard_errors[i].errmesg);
+       }
+
+       ahd_dump_card_state(ahd);
+       panic("BRKADRINT");
+
+       /* Tell everyone that this HBA is no longer availible */
+       ahd_abort_scbs(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+                      CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
+                      CAM_NO_HBA);
+
+       /* Tell the system that this controller has gone away. */
+       ahd_free(ahd);
+}
+
+void
+ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
+{
+       u_int seqintcode;
+
+       /*
+        * Save the sequencer interrupt code and clear the SEQINT
+        * bit. We will unpause the sequencer, if appropriate,
+        * after servicing the request.
+        */
+       seqintcode = ahd_inb(ahd, SEQINTCODE);
+       ahd_outb(ahd, CLRINT, CLRSEQINT);
+       ahd_update_modes(ahd);
+#if AHD_DEBUG
+       if ((ahd_debug & AHD_SHOW_MISC) != 0)
+               printf("%s: Handle Seqint Called for code %d\n",
+                      ahd_name(ahd), seqintcode);
+#endif
+       switch (seqintcode) {
+       case ENTERING_NONPACK:
+       {
+               struct  scb *scb;
+               u_int   scbid;
+
+               AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+                                ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+               scbid = ahd_get_scbptr(ahd);
+               scb = ahd_lookup_scb(ahd, scbid);
+               if (scb == NULL) {
+                       /*
+                        * Somehow need to know if this
+                        * is from a selection or reselection.
+                        * From that, we can termine target
+                        * ID so we at least have an I_T nexus.
+                        */
+               } else {
+                       ahd_outb(ahd, SAVED_SCSIID, scb->hscb->scsiid);
+                       ahd_outb(ahd, SAVED_LUN, scb->hscb->lun);
+                       ahd_outb(ahd, SEQ_FLAGS, 0x0);
+               }
+               if ((ahd_inb(ahd, LQISTAT2) & LQIPHASE_OUTPKT) != 0
+                && (ahd_inb(ahd, SCSISIGO) & ATNO) != 0) {
+                       /*
+                        * Phase change after read stream with
+                        * CRC error with P0 asserted on last
+                        * packet.
+                        */
+                       printf("Assuming LQIPHASE_NLQ with P0 assertion\n");
+               }
+               printf("Entering NONPACK\n");
+               break;
+       }
+       case INVALID_SEQINT:
+               printf("%s: Invalid Sequencer interrupt occurred.\n",
+                      ahd_name(ahd));
+               ahd_dump_card_state(ahd);
+               printf("invalid seqint");
+               ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+               break;
+       case STATUS_OVERRUN:
+       {
+               printf("%s: Status Overrun", ahd_name(ahd));
+               ahd_dump_card_state(ahd);
+               ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+               break;
+       }
+       case CFG4ISTAT_INTR:
+       {
+               struct  scb *scb;
+               u_int   scbid;
+
+               ahd_update_modes(ahd);
+               scbid = ahd_get_scbptr(ahd);
+               scb = ahd_lookup_scb(ahd, scbid);
+               if (scb == NULL) {
+                       ahd_dump_card_state(ahd);
+                       printf("CFG4ISTAT: Free SCB %d referenced", scbid);
+                       panic("For safety");
+               }
+               ahd_outq(ahd, HADDR, scb->sense_busaddr);
+               ahd_outw(ahd, HCNT, AHD_SENSE_BUFSIZE);
+               ahd_outb(ahd, HCNT + 2, 0);
+               ahd_outb(ahd, SG_CACHE_PRE, SG_LAST_SEG);
+               ahd_outb(ahd, DFCNTRL, PRELOADEN|SCSIEN|HDMAEN);
+               break;
+       }
+       case ILLEGAL_PHASE:
+       {
+               u_int bus_phase;
+
+               bus_phase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+               printf("%s: ILLEGAL_PHASE 0x%x\n",
+                      ahd_name(ahd), bus_phase);
+
+               switch (bus_phase) {
+               case P_DATAOUT:
+               case P_DATAIN:
+               case P_DATAOUT_DT:
+               case P_DATAIN_DT:
+               case P_MESGOUT:
+               case P_STATUS:
+               case P_MESGIN:
+                       ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+                       printf("%s: Issued Bus Reset.\n", ahd_name(ahd));
+                       break;
+               case P_COMMAND:
+               {
+                       struct  ahd_devinfo devinfo;
+                       struct  scb *scb;
+                       struct  ahd_initiator_tinfo *targ_info;
+                       struct  ahd_tmode_tstate *tstate;
+                       struct  ahd_transinfo *tinfo;
+                       u_int   scbid;
+
+                       /*
+                        * If a target takes us into the command phase
+                        * assume that it has been externally reset and
+                        * has thus lost our previous packetized negotiation
+                        * agreement.  Since we have not sent an identify
+                        * message and may not have fully qualified the
+                        * connection, we change our command to TUR, assert
+                        * ATN and ABORT the task when we go to message in
+                        * phase.  The OSM will see the REQUEUE_REQUEST
+                        * status and retry the command.
+                        */
+                       scbid = ahd_get_scbptr(ahd);
+                       scb = ahd_lookup_scb(ahd, scbid);
+                       if (scb == NULL) {
+                               printf("Invalid phase with no valid SCB.  "
+                                      "Resetting bus.\n");
+                               ahd_reset_channel(ahd, 'A',
+                                                 /*Initiate Reset*/TRUE);
+                               break;
+                       }
+                       ahd_compile_devinfo(&devinfo, SCB_GET_OUR_ID(scb),
+                                           SCB_GET_TARGET(ahd, scb),
+                                           SCB_GET_LUN(scb),
+                                           SCB_GET_CHANNEL(ahd, scb),
+                                           ROLE_INITIATOR);
+                       targ_info = ahd_fetch_transinfo(ahd,
+                                                       devinfo.channel,
+                                                       devinfo.our_scsiid,
+                                                       devinfo.target,
+                                                       &tstate);
+                       tinfo = &targ_info->curr;
+                       ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+                                     AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+                                     /*paused*/TRUE);
+                       ahd_set_syncrate(ahd, &devinfo, /*period*/0,
+                                        /*offset*/0, /*ppr_options*/0,
+                                        AHD_TRANS_ACTIVE, /*paused*/TRUE);
+                       ahd_outb(ahd, SCB_CDB_STORE, 0);
+                       ahd_outb(ahd, SCB_CDB_STORE+1, 0);
+                       ahd_outb(ahd, SCB_CDB_STORE+2, 0);
+                       ahd_outb(ahd, SCB_CDB_STORE+3, 0);
+                       ahd_outb(ahd, SCB_CDB_STORE+4, 0);
+                       ahd_outb(ahd, SCB_CDB_STORE+5, 0);
+                       ahd_outb(ahd, SCB_CDB_LEN, 6);
+                       scb->hscb->control &= ~(TAG_ENB|SCB_TAG_TYPE);
+                       scb->hscb->control |= MK_MESSAGE;
+                       ahd_outb(ahd, SCB_CONTROL, scb->hscb->control);
+                       ahd_outb(ahd, MSG_OUT, HOST_MSG);
+                       ahd_outb(ahd, SAVED_SCSIID, scb->hscb->scsiid);
+                       /*
+                        * The lun is 0, regardless of the SCB's lun
+                        * as we have not sent an identify message.
+                        */
+                       ahd_outb(ahd, SAVED_LUN, 0);
+                       ahd_outb(ahd, SEQ_FLAGS, 0);
+                       ahd_assert_atn(ahd);
+                       scb->flags &= ~(SCB_PACKETIZED);
+                       scb->flags |= SCB_ABORT|SCB_CMDPHASE_ABORT;
+                       ahd_freeze_devq(ahd, scb);
+                       ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
+                       ahd_freeze_scb(scb);
+
+                       /*
+                        * Allow the sequencer to continue with
+                        * non-pack processing.
+                        */
+                       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+                       ahd_outb(ahd, CLRLQOINT1, CLRLQOPHACHGINPKT);
+                       if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
+                               ahd_outb(ahd, CLRLQOINT1, 0);
+                       }
+                       printf("Continuing non-pack processing...\n");
+                       break;
+               }
+               }
+               break;
+       }
+       case CFG4OVERRUN:
+               printf("%s: CFG4OVERRUN mode = %x\n", ahd_name(ahd),
+                      ahd_inb(ahd, MODE_PTR));
+               break;
+       case DUMP_CARD_STATE:
+       {
+               ahd_dump_card_state(ahd);
+               break;
+       }
+       case PDATA_REINIT:
+       {
+               printf("%s: PDATA_REINIT - DFCNTRL = 0x%x "
+                      "SG_CACHE_SHADOW = 0x%x\n",
+                      ahd_name(ahd), ahd_inb(ahd, DFCNTRL),
+                      ahd_inb(ahd, SG_CACHE_SHADOW));
+               ahd_reinitialize_dataptrs(ahd);
+               break;
+       }
+       case HOST_MSG_LOOP:
+       {
+               struct ahd_devinfo devinfo;
+
+               /*
+                * The sequencer has encountered a message phase
+                * that requires host assistance for completion.
+                * While handling the message phase(s), we will be
+                * notified by the sequencer after each byte is
+                * transfered so we can track bus phase changes.
+                *
+                * If this is the first time we've seen a HOST_MSG_LOOP
+                * interrupt, initialize the state of the host message
+                * loop.
+                */
+               ahd_fetch_devinfo(ahd, &devinfo);
+               if (ahd->msg_type == MSG_TYPE_NONE) {
+                       struct scb *scb;
+                       u_int scb_index;
+                       u_int bus_phase;
+
+                       bus_phase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+                       if (bus_phase != P_MESGIN
+                        && bus_phase != P_MESGOUT) {
+                               printf("ahd_intr: HOST_MSG_LOOP bad "
+                                      "phase 0x%x\n",
+                                     bus_phase);
+                               /*
+                                * Probably transitioned to bus free before
+                                * we got here.  Just punt the message.
+                                */
+                               ahd_dump_card_state(ahd);
+                               ahd_clear_intstat(ahd);
+                               ahd_restart(ahd);
+                               return;
+                       }
+
+                       scb_index = ahd_get_scbptr(ahd);
+                       scb = ahd_lookup_scb(ahd, scb_index);
+                       if (devinfo.role == ROLE_INITIATOR) {
+                               if (bus_phase == P_MESGOUT)
+                                       ahd_setup_initiator_msgout(ahd,
+                                                                  &devinfo,
+                                                                  scb);
+                               else {
+                                       ahd->msg_type =
+                                           MSG_TYPE_INITIATOR_MSGIN;
+                                       ahd->msgin_index = 0;
+                               }
+                       }
+#if AHD_TARGET_MODE
+                       else {
+                               if (bus_phase == P_MESGOUT) {
+                                       ahd->msg_type =
+                                           MSG_TYPE_TARGET_MSGOUT;
+                                       ahd->msgin_index = 0;
+                               }
+                               else 
+                                       ahd_setup_target_msgin(ahd,
+                                                              &devinfo,
+                                                              scb);
+                       }
+#endif
+               }
+
+               ahd_handle_message_phase(ahd);
+               break;
+       }
+       case NO_MATCH:
+       {
+               /* Ensure we don't leave the selection hardware on */
+               AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+               ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+
+               printf("%s:%c:%d: no active SCB for reconnecting "
+                      "target - issuing BUS DEVICE RESET\n",
+                      ahd_name(ahd), 'A', ahd_inb(ahd, SELID));
+               printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+                      "ARG_1 == 0x%x ACCUM = 0x%x\n",
+                      ahd_inb(ahd, SAVED_SCSIID), ahd_inb(ahd, SAVED_LUN),
+                      ahd_inb(ahd, ARG_1), ahd_inb(ahd, ACCUM));
+               printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+                      "SINDEX == 0x%x\n",
+                      ahd_inb(ahd, SEQ_FLAGS), ahd_get_scbptr(ahd),
+                      ahd_find_busy_tcl(ahd,
+                           BUILD_TCL(ahd_inb(ahd, SAVED_SCSIID),
+                                     ahd_inb(ahd, SAVED_LUN))),
+                      ahd_inb(ahd, SINDEX));
+               printf("SELID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+                      "SCB_CONTROL == 0x%x\n",
+                      ahd_inb(ahd, SELID), ahd_inb_scbram(ahd, SCB_SCSIID),
+                      ahd_inb_scbram(ahd, SCB_LUN),
+                      ahd_inb_scbram(ahd, SCB_CONTROL));
+               printf("SCSIBUS[0] == 0x%x, SCSISIGI == 0x%x\n",
+                      ahd_inb(ahd, SCSIBUS), ahd_inb(ahd, SCSISIGI));
+               printf("SXFRCTL0 == 0x%x\n", ahd_inb(ahd, SXFRCTL0));
+               printf("SEQCTL0 == 0x%x\n", ahd_inb(ahd, SEQCTL0));
+               ahd_dump_card_state(ahd);
+               ahd->msgout_buf[0] = MSG_BUS_DEV_RESET;
+               ahd->msgout_len = 1;
+               ahd->msgout_index = 0;
+               ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+               ahd_outb(ahd, MSG_OUT, HOST_MSG);
+               ahd_assert_atn(ahd);
+               break;
+       }
+       case PROTO_VIOLATION:
+       {
+               struct  scb *scb;
+               u_int   scbid;
+               u_int   scsiid;
+               u_int   target;
+               u_int   seq_flags;
+               u_int   curphase;
+               int     found;
+
+               scbid = ahd_get_scbptr(ahd);
+               scb = ahd_lookup_scb(ahd, scbid);
+               scsiid = ahd_inb(ahd, SAVED_SCSIID);
+               target = SCSIID_TARGET(ahd, scsiid);
+               seq_flags = ahd_inb(ahd, SEQ_FLAGS);
+               curphase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+               if ((seq_flags & NOT_IDENTIFIED) != 0) {
+
+                       /*
+                        * The reconnecting target either did not send an
+                        * identify message, or did, but we didn't find an SCB
+                        * to match.
+                        */
+                       printf("%s:%c:%d: Target did not send an "
+                              "IDENTIFY message. LASTPHASE = 0x%x, "
+                              "SAVED_SCSIID == 0x%x\n", ahd_name(ahd),
+                              'A', target, ahd_inb(ahd, LASTPHASE),
+                              scsiid);
+               } else if (scb == NULL) {
+                       /*
+                        * We don't seem to have an SCB active for this
+                        * transaction.  Print an error and reset the bus.
+                        */
+                       printf("%s:%c:%d: No SCB found during protocol "
+                              "violation\n", ahd_name(ahd), 'A', target);
+                       goto proto_violation_reset;
+               } else if ((seq_flags & NO_CDB_SENT) != 0) {
+                       ahd_print_path(ahd, scb);
+                       printf("No or incomplete CDB sent to device.\n");
+               } else if ((ahd_inb(ahd, SCB_CONTROL) & STATUS_RCVD) == 0) {
+                       /*
+                        * The target never bothered to provide status to
+                        * us prior to completing the command.  Since we don't
+                        * know the disposition of this command, we must attempt
+                        * to abort it.  Assert ATN and prepare to send an abort
+                        * message.
+                        */
+                       ahd_print_path(ahd, scb);
+                       printf("Completed command without status.\n");
+               } else {
+                       ahd_print_path(ahd, scb);
+                       printf("Unknown protocol violation.\n");
+                       ahd_dump_card_state(ahd);
+               }
+               if ((curphase & ~P_DATAIN_DT) == 0) {
+proto_violation_reset:
+                       /*
+                        * Target either went directly to data
+                        * phase or didn't respond to our ATN.
+                        * The only safe thing to do is to blow
+                        * it away with a bus reset.
+                        */
+                       found = ahd_reset_channel(ahd, 'A', TRUE);
+                       printf("%s: Issued Channel %c Bus Reset. "
+                              "%d SCBs aborted\n", ahd_name(ahd), 'A', found);
+               } else {
+                       /*
+                        * Leave the selection hardware off in case
+                        * this abort attempt will affect yet to
+                        * be sent commands.
+                        */
+                       ahd_outb(ahd, SCSISEQ0,
+                                ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+                       ahd_print_path(ahd, scb);
+                       printf("Protocol violation %s.  Attempting to abort.\n",
+                              ahd_lookup_phase_entry(curphase)->phasemsg);
+                       scb->flags |= SCB_ABORT;
+                       ahd_assert_atn(ahd);
+                       ahd_outb(ahd, MSG_OUT, HOST_MSG);
+               }
+               return;
+       }
+       case IGN_WIDE_RES:
+       {
+               struct ahd_devinfo devinfo;
+
+               ahd_fetch_devinfo(ahd, &devinfo);
+               ahd_handle_ign_wide_residue(ahd, &devinfo);
+               break;
+       }
+       case BAD_PHASE:
+       {
+               u_int lastphase;
+
+               lastphase = ahd_inb(ahd, LASTPHASE);
+               printf("%s:%c:%d: unknown scsi bus phase %x, "
+                      "lastphase = 0x%x.  Attempting to continue\n",
+                      ahd_name(ahd), 'A',
+                      SCSIID_TARGET(ahd, ahd_inb(ahd, SAVED_SCSIID)),
+                      lastphase, ahd_inb(ahd, SCSISIGI));
+               break;
+       }
+       case MISSED_BUSFREE:
+       {
+               u_int lastphase;
+
+               lastphase = ahd_inb(ahd, LASTPHASE);
+               printf("%s:%c:%d: Missed busfree. "
+                      "Lastphase = 0x%x, Curphase = 0x%x\n",
+                      ahd_name(ahd), 'A',
+                      SCSIID_TARGET(ahd, ahd_inb(ahd, SAVED_SCSIID)),
+                      lastphase, ahd_inb(ahd, SCSISIGI));
+               ahd_restart(ahd);
+               return;
+       }
+       case DATA_OVERRUN:
+       {
+               /*
+                * When the sequencer detects an overrun, it
+                * places the controller in "BITBUCKET" mode
+                * and allows the target to complete its transfer.
+                * Unfortunately, none of the counters get updated
+                * when the controller is in this mode, so we have
+                * no way of knowing how large the overrun was.
+                */
+               struct  scb *scb;
+               u_int   scbindex = ahd_get_scbptr(ahd);
+               u_int   lastphase = ahd_inb(ahd, LASTPHASE);
+
+               scb = ahd_lookup_scb(ahd, scbindex);
+               ahd_print_path(ahd, scb);
+               printf("data overrun detected %s."
+                      "  Tag == 0x%x.\n",
+                      ahd_lookup_phase_entry(lastphase)->phasemsg,
+                      SCB_GET_TAG(scb));
+               ahd_print_path(ahd, scb);
+               printf("%s seen Data Phase.  Length = %ld.  NumSGs = %d.\n",
+                      ahd_inb(ahd, SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+                      ahd_get_transfer_length(scb), scb->sg_count);
+               ahd_dump_sglist(scb);
+
+               /*
+                * Set this and it will take effect when the
+                * target does a command complete.
+                */
+               ahd_freeze_devq(ahd, scb);
+               ahd_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+               ahd_freeze_scb(scb);
+               break;
+       }
+       case MKMSG_FAILED:
+       {
+               struct ahd_devinfo devinfo;
+               struct scb *scb;
+               u_int scbid;
+
+               ahd_fetch_devinfo(ahd, &devinfo);
+               printf("%s:%c:%d:%d: Attempt to issue message failed\n",
+                      ahd_name(ahd), devinfo.channel, devinfo.target,
+                      devinfo.lun);
+               scbid = ahd_get_scbptr(ahd);
+               scb = ahd_lookup_scb(ahd, scbid);
+               if (scb != NULL
+                && (scb->flags & SCB_RECOVERY_SCB) != 0)
+                       /*
+                        * Ensure that we didn't put a second instance of this
+                        * SCB into the QINFIFO.
+                        */
+                       ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb),
+                                          SCB_GET_CHANNEL(ahd, scb),
+                                          SCB_GET_LUN(scb), SCB_GET_TAG(scb),
+                                          ROLE_INITIATOR, /*status*/0,
+                                          SEARCH_REMOVE);
+               ahd_outb(ahd, SCB_CONTROL,
+                        ahd_inb(ahd, SCB_CONTROL) & ~MK_MESSAGE);
+               break;
+       }
+       }
+       /*
+        *  The sequencer is paused immediately on
+        *  a SEQINT, so we should restart it when
+        *  we're done.
+        */
+       ahd_unpause(ahd);
+}
+
+void
+ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat)
+{
+       struct scb      *scb;
+       u_int            status0;
+       u_int            status3;
+       u_int            status;
+       u_int            lqistat1;
+       u_int            lqostat0;
+       u_int            scbid;
+
+       ahd_update_modes(ahd);
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+       status3 = ahd_inb(ahd, SSTAT3) & (NTRAMPERR|OSRAMPERR);
+       status0 = ahd_inb(ahd, SSTAT0) & (IOERR|OVERRUN|SELDI|SELDO);
+       status = ahd_inb(ahd, SSTAT1) & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+       lqistat1 = ahd_inb(ahd, LQISTAT1);
+       lqostat0 = ahd_inb(ahd, LQOSTAT0);
+       if ((status0 & (SELDI|SELDO)) != 0) {
+               u_int simode0;
+
+               ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+               simode0 = ahd_inb(ahd, SIMODE0);
+               status0 &= simode0 & (ENSELDO|ENSELDI|IOERR);
+               ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       }
+       scbid = ahd_get_scbptr(ahd);
+       scb = ahd_lookup_scb(ahd, scbid);
+       if (scb != NULL
+        && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
+               scb = NULL;
+
+       /* Make sure the sequencer is in a safe location. */
+       ahd_clear_critical_section(ahd);
+
+       if ((status0 & IOERR) != 0) {
+               int now_lvd;
+
+               now_lvd = ahd_inb(ahd, SBLKCTL) & ENAB40;
+               printf("%s: Transceiver State Has Changed to %s mode\n",
+                      ahd_name(ahd), now_lvd ? "LVD" : "SE");
+               ahd_outb(ahd, CLRSINT0, CLRIOERR);
+/* XXX Still True?? */
+               /*
+                * When transitioning to SE mode, the reset line
+                * glitches, triggering an arbitration bug in some
+                * Ultra2 controllers.  This bug is cleared when we
+                * assert the reset line.  Since a reset glitch has
+                * already occurred with this transition and a
+                * transceiver state change is handled just like
+                * a bus reset anyway, asserting the reset line
+                * ourselves is safe.
+                */
+               ahd_reset_channel(ahd, 'A', /*Initiate Reset*/now_lvd == 0);
+
+               ahd_pause(ahd);
+               ahd_setup_iocell_workaround(ahd);
+               ahd_unpause(ahd);
+       } else if ((status0 & OVERRUN) != 0) {
+               printf("%s: SCSI offset overrun detected.  Resetting bus.\n",
+                      ahd_name(ahd));
+               ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+       } else if ((status & SCSIRSTI) != 0) {
+               printf("%s: Someone reset channel A\n", ahd_name(ahd));
+               ahd_reset_channel(ahd, 'A', /*Initiate Reset*/FALSE);
+       } else if ((status & SCSIPERR) != 0) {
+               ahd_handle_transmission_error(ahd);
+       } else if (lqostat0 != 0) {
+               printf("%s: lqostat0 == 0x%x!\n", ahd_name(ahd), lqostat0);
+               ahd_outb(ahd, CLRLQOINT0, lqostat0);
+               if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
+                       ahd_outb(ahd, CLRLQOINT1, 0);
+               }
+       } else if ((status & SELTO) != 0) {
+               u_int scbid;
+
+               /* Stop the selection */
+               ahd_outb(ahd, SCSISEQ0, 0);
+
+               /* No more pending messages */
+               ahd_clear_msg_state(ahd);
+
+               /* Clear interrupt state */
+               ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) & ~ENBUSFREE);
+               ahd_outb(ahd, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
+
+               /*
+                * Although the driver does not care about the
+                * 'Selection in Progress' status bit, the busy
+                * LED does.  SELINGO is only cleared by a sucessfull
+                * selection, so we must manually clear it to insure
+                * the LED turns off just incase no future successful
+                * selections occur (e.g. no devices on the bus).
+                */
+               ahd_outb(ahd, CLRSINT0, CLRSELINGO);
+
+               scbid = ahd_inw(ahd, WAITING_TID_HEAD);
+#if AHD_DEBUG
+               if ((ahd_debug & AHD_SHOW_SELTO) != 0)
+                       printf("%s: Saw Selection Timeout for SCB 0x%x\n",
+                              ahd_name(ahd), scbid);
+#endif
+               scb = ahd_lookup_scb(ahd, scbid);
+               if (scb == NULL) {
+                       printf("%s: ahd_intr - referenced scb not "
+                              "valid during SELTO scb(0x%x)\n",
+                              ahd_name(ahd), scbid);
+                       ahd_dump_card_state(ahd);
+                       panic("For diagnostics");
+               } else {
+                       ahd_set_transaction_status(scb, CAM_SEL_TIMEOUT);
+                       ahd_freeze_devq(ahd, scb);
+               }
+               ahd_outb(ahd, CLRINT, CLRSCSIINT);
+               ahd_iocell_first_selection(ahd);
+               ahd_restart(ahd);
+       } else if ((status0 & (SELDI|SELDO)) != 0) {
+               ahd_iocell_first_selection(ahd);
+               ahd_unpause(ahd);
+       } else if (status3 != 0) {
+               printf("%s: SCSI Cell parity error SSTAT3 == 0x%x\n",
+                      ahd_name(ahd), status3);
+               ahd_outb(ahd, CLRSINT3, status3);
+       } else if ((lqistat1 & (LQIPHASE_LQ|LQIPHASE_NLQ)) != 0) {
+               ahd_handle_lqiphase_error(ahd, lqistat1);
+       } else if ((status & BUSFREE) != 0) {
+               u_int busfreetime;
+               u_int lqostat1;
+               int   restart;
+               int   clear_fifo;
+               int   packetized;
+               u_int mode;
+
+               /*
+                * Clear our selection hardware as soon as possible.
+                * We may have an entry in the waiting Q for this target,
+                * that is affected by this busfree and we don't want to
+                * go about selecting the target while we handle the event.
+                */
+               ahd_outb(ahd, SCSISEQ0, 0);
+
+               /*
+                * Determine what we were up to at the time of
+                * the busfree.
+                */
+               mode = AHD_MODE_SCSI;
+               ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+               busfreetime = ahd_inb(ahd, SSTAT2) & BUSFREETIME;
+               lqostat1 = ahd_inb(ahd, LQOSTAT1);
+               switch (busfreetime) {
+               case BUSFREE_DFF0:
+               case BUSFREE_DFF1:
+               {
+                       u_int   scbid;
+                       struct  scb *scb;
+
+                       mode = busfreetime == BUSFREE_DFF0
+                            ? AHD_MODE_DFF0 : AHD_MODE_DFF1;
+                       ahd_set_modes(ahd, mode, mode);
+                       scbid = ahd_get_scbptr(ahd);
+                       scb = ahd_lookup_scb(ahd, scbid);
+                       if (scb == NULL) {
+                               printf("%s: Invalid SCB in DFF%d "
+                                      "during unexpected busfree\n",
+                                      ahd_name(ahd), mode);
+                               packetized = 0;
+                       } else
+                               packetized = (scb->flags & SCB_PACKETIZED) != 0;
+                       clear_fifo = 1;
+                       break;
+               }
+               case BUSFREE_LQO:
+                       clear_fifo = 0;
+                       packetized = 1;
+                       break;
+               default:
+                       clear_fifo = 0;
+                       packetized =  (lqostat1 & LQOBUSFREE) != 0;
+                       if (!packetized
+                        && ahd_inb(ahd, LASTPHASE) == P_BUSFREE)
+                               packetized = 1;
+                       break;
+               }
+
+#if AHD_DEBUG
+               if ((ahd_debug & AHD_SHOW_MISC) != 0)
+                       printf("Saw Busfree.  Busfreetime = 0x%x.\n",
+                              busfreetime);
+#endif
+               /*
+                * Busfrees that occur in non-packetized phases are
+                * handled by the nonpkt_busfree handler.
+                */
+               if (packetized && ahd_inb(ahd, LASTPHASE) == P_BUSFREE) {
+                       restart = ahd_handle_pkt_busfree(ahd, busfreetime);
+               } else {
+                       restart = ahd_handle_nonpkt_busfree(ahd);
+               }
+               /*
+                * Clear the busfree interrupt status.  The setting of
+                * the interrupt is a pulse, so we do not need to muck
+                * with the ENBUSFREE logic.  This also ensures that if
+                * the bus has moved on to another connection, busfree
+                * protection is still in force.
+                */
+               ahd_outb(ahd, CLRSINT1, CLRBUSFREE|CLRSCSIPERR);
+
+               if (clear_fifo)
+                       ahd_clear_fifo(ahd, mode);
+
+               ahd_clear_msg_state(ahd);
+               ahd_outb(ahd, CLRINT, CLRSCSIINT);
+               if (restart)
+                       ahd_restart(ahd);
+               else {
+                       ahd_unpause(ahd);
+               }
+       } else {
+               printf("%s: Missing case in ahd_handle_scsiint. status = %x\n",
+                      ahd_name(ahd), status);
+               printf("%s: lqostat1 == 0x%x, SIMODE1 == 0x%x\n",
+                      ahd_name(ahd), ahd_inb(ahd, LQOSTAT1),
+                      ahd_inb(ahd, SIMODE1));
+               ahd_outb(ahd, CLRINT, CLRSCSIINT);
+               ahd_dump_card_state(ahd);
+               panic("Missing SCSIINT case");
+               ahd_unpause(ahd);
+       }
+}
+
+static void
+ahd_handle_transmission_error(struct ahd_softc *ahd)
+{
+       u_int            lqistat1;
+       u_int            lqistat2;
+       u_int            msg_out;
+       u_int            curphase;
+       u_int            lastphase;
+       u_int            perrdiag;
+
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       lqistat1 = ahd_inb(ahd, LQISTAT1) & ~(LQIPHASE_LQ|LQIPHASE_NLQ);
+       lqistat2 = ahd_inb(ahd, LQISTAT2);
+       if ((lqistat1 & (LQICRCI_NLQ|LQICRCI_LQ)) == 0
+        && (ahd->bugs & AHD_NLQICRC_DELAYED_BUG) != 0) {
+               u_int lqistate;
+
+               ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+               lqistate = ahd_inb(ahd, LQISTATE);
+               if ((lqistate >= 0x1E && lqistate <= 0x24)
+                || (lqistate == 0x29)) {
+                       printf("%s: NLQCRC found via LQISTATE\n",
+                              ahd_name(ahd));
+                       lqistat1 |= LQICRCI_NLQ;
+               }
+               ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       }
+
+       ahd_outb(ahd, CLRLQIINT1, lqistat1);
+       lastphase = ahd_inb(ahd, LASTPHASE);
+       curphase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
+       perrdiag = ahd_inb(ahd, PERRDIAG);
+       msg_out = MSG_INITIATOR_DET_ERR;
+       ahd_outb(ahd, CLRSINT1, CLRSCSIPERR);
+       printf("%s: Transmission error detected\n", ahd_name(ahd));
+       printf("%s: lqistat1 == 0x%x, LASTPHASE == 0x0%x, "
+              "curphase = 0x%x, perrdiag == 0x%x\n",
+              ahd_name(ahd), lqistat1, lastphase, curphase, perrdiag);
+       ahd_dump_card_state(ahd);
+       if ((lqistat1 & (LQIOVERI_LQ|LQIOVERI_NLQ)) != 0) {
+               printf("%s: Gross protocol error during incoming "
+                      "packet.  lqistat1 == 0x%x.  Resetting bus.\n",
+                      ahd_name(ahd), lqistat1);
+               ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+               return;
+       } else if ((lqistat1 & LQICRCI_LQ) != 0) {
+               /*
+                * A CRC error has been detected on an incoming LQ.
+                * The bus is currently hung on the last ACK.
+                * Hit LQIRETRY to release the last ack, and
+                * wait for the sequencer to determine that ATNO
+                * is asserted while in message out to take us
+                * to our host message loop.  No NONPACKREQ or
+                * LQIPHASE type errors will occur in this
+                * scenario.  After this first LQIRETRY, the LQI
+                * manager will be in ISELO where it will
+                * happily sit until another packet phase begins.
+                * Unexpected bus free detection is enabled
+                * through any phases that occur after we release
+                * this last ack until the LQI manager sees a
+                * packet phase.  This implies we may have to
+                * ignore a perfectly valid "unexected busfree"
+                * after our "initiator detected error" message is
+                * sent.  A busfree is the expected response after
+                * we tell the target that it's L_Q was corrupted.
+                * (SPI4R09 10.7.3.3.3)
+                */
+               ahd_outb(ahd, LQCTL2, LQIRETRY);
+               printf("LQIRetry for LQICRCI_LQ to release ACK\n");
+       } else if ((lqistat1 & LQICRCI_NLQ) != 0) {
+               u_int scbid;
+               struct scb *scb;
+
+               /*
+                * We detected a CRC error in a NON-LQ packet.
+                * The hardware has varying behavior in this situation
+                * depending on whether this packet was part of a
+                * stream or not.
+                *
+                * PKT by PKT mode:
+                * The hardware has already acked the complete packet.
+                * If the target honors our outstanding ATN condition,
+                * we should be (or soon will be) in MSGOUT phase.
+                * This will trigger the LQIPHASE_LQ status bit as the
+                * hardware was expecting another LQ.  Unexpected
+                * busfree detection is enabled.  Once LQIPHASE_LQ is
+                * true (first entry into host message loop is much
+                * the same), we must clear LQIPHASE_LQ and hit
+                * LQIRETRY so the hardware is ready to handle
+                * a future LQ.  NONPACKREQ will not be asserted again
+                * once we hit LQIRETRY until another packet is
+                * processed.  The target may either go busfree
+                * or start another packet in response to our message.
+                *
+                * Read Streaming P0 asserted:
+                * If we raise ATN and the target completes the entire
+                * stream (P0 asserted during the last packet), the
+                * hardware will ack all data and return to the ISTART
+                * state.  When the target reponds to our ATN condition,
+                * LQIPHASE_LQ will be asserted.  We should respond to
+                * this with an LQIRETRY to prepare for any future
+                * packets.  NONPACKREQ will not be asserted again
+                * once we hit LQIRETRY until another packet is
+                * processed.  The target may either go busfree or
+                * start another packet in response to our message.
+                * Busfree detection is enabled.
+                *
+                * Read Streaming P0 not asserted:
+                * If we raise ATN and the target transitions to
+                * MSGOUT in or after a packet where P0 is not
+                * asserted, the hardware will assert LQIPHASE_NLQ.
+                * We should respond to the LQIPHASE_NLQ with an
+                * LQICONTINUE.  Should the target stay in a non-pkt
+                * phase after we send our message, the hardware
+                * will assert LQIPHASE_LQ.  Recovery is then just as
+                * listed above for the read streaming with P0 asserted.
+                * Busfree detection is enabled.
+                */
+               printf("LQICRC_NLQ\n");
+               ahd_set_active_fifo(ahd);
+               scbid = ahd_get_scbptr(ahd);
+               scb = ahd_lookup_scb(ahd, scbid);
+               if (scb == NULL) {
+                       printf("%s: No SCB valid for LQICRC_NLQ.  "
+                              "Resetting bus\n", ahd_name(ahd));
+                       ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+                       return;
+               }
+               scb->flags |= SCB_TRANSMISSION_ERROR;
+       } else if ((lqistat1 & LQIBADLQI) != 0) {
+               printf("Need to handle BADLQI!\n");
+               ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+               return;
+       } else if ((perrdiag & (PARITYERR|LASTPHASE)) == PARITYERR) {
+               if ((curphase & ~P_DATAIN_DT) != 0) {
+                       /* Ack the byte.  So we can continue. */
+                       printf("Acking %s to clear perror\n",
+                              ahd_lookup_phase_entry(curphase)->phasemsg);
+                       ahd_inb(ahd, SCSIDAT);
+               }
+       
+               if (curphase == P_MESGIN)
+                       msg_out = MSG_PARITY_ERROR;
+       }
+
+       /*
+        * We've set the hardware to assert ATN if we 
+        * get a parity error on "in" phases, so all we
+        * need to do is stuff the message buffer with
+        * the appropriate message.  "In" phases have set
+        * mesg_out to something other than MSG_NOP.
+        */
+       ahd->send_msg_perror = msg_out;
+       ahd_outb(ahd, MSG_OUT, HOST_MSG);
+       ahd_outb(ahd, CLRINT, CLRSCSIINT);
+       ahd_unpause(ahd);
+}
+
+static void
+ahd_handle_lqiphase_error(struct ahd_softc *ahd, u_int lqistat1)
+{
+       /*
+        * Clear the sources of the interrupts.
+        */
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       ahd_outb(ahd, CLRLQIINT1, lqistat1);
+
+       /*
+        * If the "illegal" phase changes were in response
+        * to our ATN to flag a CRC error, AND we ended up
+        * on packet boundaries, clear the error, restart the
+        * LQI manager as appropriate, and go on our merry
+        * way toward sending the message.  Otherwise, reset
+        * the bus to clear the error.
+        */
+       ahd_set_active_fifo(ahd);
+       if ((ahd_inb(ahd, SCSISIGO) & ATNO) != 0
+        && (ahd_inb(ahd, MDFFSTAT) & DLZERO) != 0) {
+               if ((lqistat1 & LQIPHASE_LQ) != 0) {
+                       printf("LQIRETRY for LQIPHASE_LQ\n");
+                       ahd_outb(ahd, LQCTL2, LQIRETRY);
+               } else if ((lqistat1 & LQIPHASE_NLQ) != 0) {
+                       printf("LQICONTINUE for LQIPHASE_NLQ\n");
+                       ahd_outb(ahd, LQCTL2, LQIRETRY);
+               } else
+                       panic("ahd_handle_lqiphase_error: No phase errors\n");
+               ahd_dump_card_state(ahd);
+               ahd_outb(ahd, CLRINT, CLRSCSIINT);
+               ahd_unpause(ahd);
+       } else {
+               printf("Reseting Channel for LQI Phase error\n");
+               ahd_dump_card_state(ahd);
+               ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
+       }
+}
+
+/*
+ * Packetized unexpected or expected busfree.
+ * Entered in MODE_SCSI.
+ */
+static int
+ahd_handle_pkt_busfree(struct ahd_softc *ahd, u_int busfreetime)
+{
+       u_int lqostat1;
+
+       AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+                        ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+       lqostat1 = ahd_inb(ahd, LQOSTAT1);
+       if ((lqostat1 & LQOBUSFREE) != 0) {
+               struct scb *scb;
+               u_int scbid;
+               u_int waiting_h;
+               u_int waiting_t;
+               u_int next;
+
+               if ((busfreetime & BUSFREE_LQO) == 0)
+                       printf("%s: Warning, BUSFREE time is 0x%x.  "
+                              "Expected BUSFREE_LQO.\n",
+                              ahd_name(ahd), busfreetime);
+
+               scbid = ahd_get_scbptr(ahd);
+               scb = ahd_lookup_scb(ahd, scbid);
+               if (scb == NULL)
+                      panic("SCB not valid during LQOBUSFREE");
+               ahd_print_path(ahd, scb);
+               printf("Probable outgoing LQ CRC error.  Retrying command\n");
+
+               /*
+                * Return the LQO manager to its idle loop.  It will
+                * not do this automatically if the busfree occurs
+                * after the first REQ of either the LQ or command
+                * packet or between the LQ and command packet.
+                */
+               ahd_outb(ahd, LQCTL2, ahd_inb(ahd, LQCTL2) | LQOTOIDLE);
+
+               /*
+                * Clear the status.
+                */
+               ahd_outb(ahd, CLRLQOINT1, CLRLQOBUSFREE);
+               if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
+                       ahd_outb(ahd, CLRLQOINT1, 0);
+               }
+               /*
+                * The LQO manager detected an unexpected busfree
+                * either:
+                *
+                * 1) During an outgoing LQ.
+                * 2) After an outgoing LQ but before the first
+                *    REQ of the command packet.
+                * 3) During an outgoing command packet.
+                *
+                * In all cases, CURRSCB is pointing to the
+                * SCB that encountered the failure.  Clean
+                * up the queue, clear SELDO and LQOBUSFREE,
+                * and allow the sequencer to restart the select
+                * out at its lesure.
+                */
+               ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
+               ahd_outb(ahd, CLRSINT0, CLRSELDO);
+               waiting_h = ahd_inw(ahd, WAITING_TID_HEAD);
+               if (waiting_h != scbid) {
+
+                       ahd_outw(ahd, WAITING_TID_HEAD, scbid);
+                       waiting_t = ahd_inw(ahd, WAITING_TID_TAIL);
+                       next = SCB_LIST_NULL;
+                       if (waiting_t == waiting_h) {
+                               ahd_outw(ahd, WAITING_TID_TAIL, scbid);
+                       } else {
+                               ahd_set_scbptr(ahd, waiting_h);
+                               next = ahd_inw(ahd, SCB_NEXT2);
+                       }
+                       ahd_set_scbptr(ahd, scbid);
+                       ahd_outw(ahd, SCB_NEXT2, next);
+               }
+
+               /* Return unpausing the sequencer. */
+               return (0);
+       }
+       if (ahd->src_mode != AHD_MODE_SCSI) {
+               u_int   scbid;
+               struct  scb *scb;
+
+               scbid = ahd_get_scbptr(ahd);
+               scb = ahd_lookup_scb(ahd, scbid);
+               ahd_print_path(ahd, scb);
+               printf("Unexpected PKT busfree condition\n");
+               ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb), 'A',
+                              SCB_GET_LUN(scb), SCB_GET_TAG(scb),
+                              ROLE_INITIATOR, CAM_UNEXP_BUSFREE);
+
+               /* Return restarting the sequencer. */
+               return (1);
+       }
+       printf("%s: Unexpected PKT busfree condition\n", ahd_name(ahd));
+       ahd_dump_card_state(ahd);
+       /* Restart the sequencer. */
+       return (1);
+}
+
+/*
+ * Non-packetized unexpected or expected busfree.
+ */
+static int
+ahd_handle_nonpkt_busfree(struct ahd_softc *ahd)
+{
+       struct  ahd_devinfo devinfo;
+       struct  scb *scb;
+       u_int   lastphase;
+       u_int   saved_scsiid;
+       u_int   saved_lun;
+       u_int   target;
+       u_int   initiator_role_id;
+       u_int   scbid;
+       int     printerror;
+
+       /*
+        * Look at what phase we were last in.  If its message out,
+        * chances are pretty good that the busfree was in response
+        * to one of our abort requests.
+        */
+       lastphase = ahd_inb(ahd, LASTPHASE);
+       saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
+       saved_lun = ahd_inb(ahd, SAVED_LUN);
+       target = SCSIID_TARGET(ahd, saved_scsiid);
+       initiator_role_id = SCSIID_OUR_ID(saved_scsiid);
+       ahd_compile_devinfo(&devinfo, initiator_role_id,
+                           target, saved_lun, 'A', ROLE_INITIATOR);
+       printerror = 1;
+
+       scbid = ahd_get_scbptr(ahd);
+       scb = ahd_lookup_scb(ahd, scbid);
+       if (scb != NULL
+        && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
+               scb = NULL;
+
+       if (lastphase == P_MESGOUT) {
+               u_int tag;
+
+               tag = SCB_LIST_NULL;
+               if (ahd_sent_msg(ahd, AHDMSG_1B, MSG_ABORT_TAG, TRUE)
+                || ahd_sent_msg(ahd, AHDMSG_1B, MSG_ABORT, TRUE)) {
+                       int found;
+                       int sent_msg;
+
+                       sent_msg = ahd->msgout_buf[ahd->msgout_index - 1];
+                       ahd_print_path(ahd, scb);
+                       printf("SCB %d - Abort%s Completed.\n",
+                              SCB_GET_TAG(scb),
+                              sent_msg == MSG_ABORT_TAG ? "" : " Tag");
+
+                       if (sent_msg == MSG_ABORT_TAG)
+                               tag = SCB_GET_TAG(scb);
+
+                       if ((scb->flags & SCB_CMDPHASE_ABORT) != 0) {
+                               /*
+                                * This abort is in response to an
+                                * unexpected switch to command phase
+                                * for a packetized connection.  Since
+                                * the identify message was never sent,
+                                * "saved lun" is 0.  We really want to
+                                * abort only the SCB that encountered
+                                * this error, which could have a different
+                                * lun.  The SCB will be retried so the OS
+                                * will see the UA after renegotiating to
+                                * packetized.
+                                */
+                               tag = SCB_GET_TAG(scb);
+                               saved_lun = scb->hscb->lun;
+                       }
+                       found = ahd_abort_scbs(ahd, target, 'A', saved_lun,
+                                              tag, ROLE_INITIATOR,
+                                              CAM_REQ_ABORTED);
+                       printf("found == 0x%x\n", found);
+                       printerror = 0;
+               } else if (ahd_sent_msg(ahd, AHDMSG_1B,
+                                       MSG_BUS_DEV_RESET, TRUE)) {
+#ifdef __FreeBSD__
+                       /*
+                        * Don't mark the user's request for this BDR
+                        * as completing with CAM_BDR_SENT.  CAM3
+                        * specifies CAM_REQ_CMP.
+                        */
+                       if (scb != NULL
+                        && scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
+                        && ahd_match_scb(ahd, scb, target, 'A',
+                                         CAM_LUN_WILDCARD, SCB_LIST_NULL,
+                                         ROLE_INITIATOR))
+                               ahd_set_transaction_status(scb, CAM_REQ_CMP);
+#endif
+                       ahd_handle_devreset(ahd, &devinfo, CAM_BDR_SENT,
+                                           "Bus Device Reset",
+                                           /*verbose_level*/0);
+                       printerror = 0;
+               } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, FALSE)) {
+                       struct ahd_initiator_tinfo *tinfo;
+                       struct ahd_tmode_tstate *tstate;
+
+                       /*
+                        * PPR Rejected.  Try non-ppr negotiation
+                        * and retry command.
+                        */
+                       tinfo = ahd_fetch_transinfo(ahd, devinfo.channel,
+                                                   devinfo.our_scsiid,
+                                                   devinfo.target, &tstate);
+                       tinfo->curr.transport_version = 2;
+                       tinfo->goal.transport_version = 2;
+                       tinfo->goal.ppr_options = 0;
+                       ahd_qinfifo_requeue_tail(ahd, scb);
+                       printerror = 0;
+               } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, FALSE)
+                       || ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, FALSE)) {
+                       /*
+                        * Negotiation Rejected.  Go-async and
+                        * retry command.
+                        */
+                       ahd_set_width(ahd, &devinfo,
+                                     MSG_EXT_WDTR_BUS_8_BIT,
+                                     AHD_TRANS_CUR|AHD_TRANS_GOAL,
+                                     /*paused*/TRUE);
+                       ahd_set_syncrate(ahd, &devinfo,
+                                       /*period*/0, /*offset*/0,
+                                       /*ppr_options*/0,
+                                       AHD_TRANS_CUR|AHD_TRANS_GOAL,
+                                       /*paused*/TRUE);
+                       ahd_qinfifo_requeue_tail(ahd, scb);
+                       printerror = 0;
+               } else if ((ahd->msg_flags & MSG_FLAG_EXPECT_IDE_BUSFREE) != 0
+                       && ahd_sent_msg(ahd, AHDMSG_1B,
+                                        MSG_INITIATOR_DET_ERR, TRUE)) {
+
+#if AHD_DEBUG
+                       if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+                               printf("Expected IDE Busfree\n");
+#endif
+                       printerror = 0;
+               }
+       } else if (lastphase == P_MESGIN) {
+
+               if ((ahd->msg_flags & MSG_FLAG_EXPECT_PPR_BUSFREE) != 0) {
+
+                       ahd_freeze_devq(ahd, scb);
+                       ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
+                       ahd_freeze_scb(scb);
+                       if ((ahd->msg_flags & MSG_FLAG_IU_REQ_CHANGED) != 0) {
+                               ahd_print_path(ahd, scb);
+                               printf("Now %spacketized.\n",
+                                      (scb->flags & SCB_PACKETIZED) == 0
+                                    ? "" : "non-");
+                               ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb),
+                                              SCB_GET_CHANNEL(ahd, scb),
+                                              SCB_GET_LUN(scb), SCB_LIST_NULL,
+                                              ROLE_INITIATOR, CAM_REQ_ABORTED);
+                       } else {
+#if AHD_DEBUG
+                               if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+                                       printf("PPR Negotiation Busfree.\n");
+#endif
+                               ahd_done(ahd, scb);
+                       }
+                       printerror = 0;
+               }
+       }
+       if (printerror != 0) {
+               int aborted;
+
+               aborted = 0;
+               if (scb != NULL) {
+                       u_int tag;
+
+                       if ((scb->hscb->control & TAG_ENB) != 0)
+                               tag = SCB_GET_TAG(scb);
+                       else
+                               tag = SCB_LIST_NULL;
+                       ahd_print_path(ahd, scb);
+                       aborted = ahd_abort_scbs(ahd, target, 'A',
+                                      SCB_GET_LUN(scb), tag,
+                                      ROLE_INITIATOR,
+                                      CAM_UNEXP_BUSFREE);
+               } else {
+                       /*
+                        * We had not fully identified this connection,
+                        * so we cannot abort anything.
+                        */
+                       printf("%s: ", ahd_name(ahd));
+               }
+               if (lastphase != P_BUSFREE)
+                       ahd_force_renegotiation(ahd, &devinfo);
+               printf("Unexpected busfree %s, %d SCBs aborted, "
+                      "PRGMCNT == 0x%x\n",
+                      ahd_lookup_phase_entry(lastphase)->phasemsg,
+                      aborted,
+                      ahd_inb(ahd, PRGMCNT)
+                       | (ahd_inb(ahd, PRGMCNT+1) << 8));
+               ahd_dump_card_state(ahd);
+       }
+       /* Always restart the sequencer. */
+       return (1);
+}
+
+/*
+ * Force renegotiation to occur the next time we initiate
+ * a command to the current device.
+ */
+static void
+ahd_force_renegotiation(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+       struct  ahd_initiator_tinfo *targ_info;
+       struct  ahd_tmode_tstate *tstate;
+
+       printf("Forcing renegotiation (%d:%c:%d)\n",
+              devinfo->our_scsiid, devinfo->channel, 
+              devinfo->target);
+       targ_info = ahd_fetch_transinfo(ahd,
+                                       devinfo->channel,
+                                       devinfo->our_scsiid,
+                                       devinfo->target,
+                                       &tstate);
+       ahd_update_neg_request(ahd, devinfo, tstate,
+                              targ_info, /*force*/TRUE);
+}
+
+#define AHD_MAX_STEPS 2000
+void
+ahd_clear_critical_section(struct ahd_softc *ahd)
+{
+       ahd_mode_state  saved_modes;
+       int             stepping;
+       int             steps;
+       u_int           simode0;
+       u_int           simode1;
+       u_int           simode3;
+       u_int           lqimode0;
+       u_int           lqimode1;
+       u_int           lqomode0;
+       u_int           lqomode1;
+
+       if (ahd->num_critical_sections == 0)
+               return;
+
+       stepping = FALSE;
+       steps = 0;
+       simode0 = 0;
+       simode1 = 0;
+       simode3 = 0;
+       lqimode0 = 0;
+       lqimode1 = 0;
+       lqomode0 = 0;
+       lqomode1 = 0;
+       saved_modes = ahd_save_modes(ahd);
+       for (;;) {
+               struct  cs *cs;
+               u_int   seqaddr;
+               u_int   i;
+
+               ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+               seqaddr = ahd_inb(ahd, CURADDR)
+                       | (ahd_inb(ahd, CURADDR+1) << 8);
+
+               cs = ahd->critical_sections;
+               for (i = 0; i < ahd->num_critical_sections; i++, cs++) {
+                       
+                       if (cs->begin < seqaddr && cs->end >= seqaddr)
+                               break;
+               }
+
+               if (i == ahd->num_critical_sections)
+                       break;
+
+               if (steps > AHD_MAX_STEPS) {
+                       printf("%s: Infinite loop in critical section\n",
+                              ahd_name(ahd));
+                       ahd_dump_card_state(ahd);
+                       panic("critical section loop");
+               }
+
+               steps++;
+               if (stepping == FALSE) {
+
+                       /*
+                        * Disable all interrupt sources so that the
+                        * sequencer will not be stuck by a pausing
+                        * interrupt condition while we attempt to
+                        * leave a critical section.
+                        */
+                       ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+                       simode0 = ahd_inb(ahd, SIMODE0);
+                       simode3 = ahd_inb(ahd, SIMODE3);
+                       lqimode0 = ahd_inb(ahd, LQIMODE0);
+                       lqimode1 = ahd_inb(ahd, LQIMODE1);
+                       lqomode0 = ahd_inb(ahd, LQOMODE0);
+                       lqomode1 = ahd_inb(ahd, LQOMODE1);
+                       ahd_outb(ahd, SIMODE0, 0);
+                       ahd_outb(ahd, SIMODE3, 0);
+                       ahd_outb(ahd, LQIMODE0, 0);
+                       ahd_outb(ahd, LQIMODE1, 0);
+                       ahd_outb(ahd, LQOMODE0, 0);
+                       ahd_outb(ahd, LQOMODE1, 0);
+                       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+                       simode1 = ahd_inb(ahd, SIMODE1);
+                       ahd_outb(ahd, SIMODE1, 0);
+                       ahd_outb(ahd, CLRINT, CLRSCSIINT);
+                       ahd_outb(ahd, SEQCTL0, ahd_inb(ahd, SEQCTL0) | STEP);
+                       stepping = TRUE;
+               }
+               ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
+               ahd_outb(ahd, HCNTRL, ahd->unpause);
+               do {
+                       ahd_delay(200);
+               } while (!ahd_is_paused(ahd));
+               ahd_update_modes(ahd);
+       }
+       if (stepping) {
+               ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+               ahd_outb(ahd, SIMODE0, simode0);
+               ahd_outb(ahd, SIMODE3, simode3);
+               ahd_outb(ahd, LQIMODE0, lqimode0);
+               ahd_outb(ahd, LQIMODE1, lqimode1);
+               ahd_outb(ahd, LQOMODE0, lqomode0);
+               ahd_outb(ahd, LQOMODE1, lqomode1);
+               ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+               ahd_outb(ahd, SIMODE1, simode1);
+               ahd_outb(ahd, SEQCTL0, ahd_inb(ahd, SEQCTL0) & ~STEP);
+       }
+       ahd_restore_modes(ahd, saved_modes);
+}
+
+/*
+ * Clear any pending interrupt status.
+ */
+void
+ahd_clear_intstat(struct ahd_softc *ahd)
+{
+       /* Clear any interrupt conditions this may have caused */
+       ahd_outb(ahd, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
+                               |CLRBUSFREE|CLRSCSIPERR|CLRREQINIT);
+       ahd_outb(ahd, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO|CLRIOERR);
+       ahd_outb(ahd, CLRINT, CLRSCSIINT);
+}
+
+/**************************** Debugging Routines ******************************/
+#ifdef AHD_DEBUG
+uint32_t ahd_debug = AHD_DEBUG;
+#endif
+void
+ahd_print_scb(struct scb *scb)
+{
+       struct hardware_scb *hscb;
+       int i;
+
+       hscb = scb->hscb;
+       printf("scb:%p control:0x%x scsiid:0x%x lun:%d cdb_len:%d\n",
+              (void *)scb,
+              hscb->control,
+              hscb->scsiid,
+              hscb->lun,
+              hscb->cdb_len);
+       printf("Shared Data: ");
+       for (i = 0; i < sizeof(hscb->shared_data.idata.cdb); i++)
+               printf("%#02x", hscb->shared_data.idata.cdb[i]);
+       printf("        dataptr:%#x%x datacnt:%#x sgptr:%#x tag:%#x\n",
+              (uint32_t)((ahd_le64toh(hscb->dataptr) >> 32) & 0xFFFFFFFF),
+              (uint32_t)(ahd_le64toh(hscb->dataptr) & 0xFFFFFFFF),
+              ahd_le32toh(hscb->datacnt),
+              ahd_le32toh(hscb->sgptr),
+              SCB_GET_TAG(scb));
+       ahd_dump_sglist(scb);
+}
+
+void
+ahd_dump_sglist(struct scb *scb)
+{
+       int i;
+
+       if (scb->sg_count > 0) {
+               if ((scb->ahd_softc->flags & AHD_64BIT_ADDRESSING) != 0) {
+                       struct ahd_dma64_seg *sg_list;
+
+                       sg_list = (struct ahd_dma64_seg*)scb->sg_list;
+                       for (i = 0; i < scb->sg_count; i++) {
+                               uint64_t addr;
+
+                               addr = ahd_le64toh(sg_list[i].addr);
+                               printf("sg[%d] - Addr 0x%x%x : Length %d\n",
+                                      i,
+                                      (uint32_t)((addr >> 32) & 0xFFFFFFFF),
+                                      (uint32_t)(addr & 0xFFFFFFFF),
+                                      ahd_le32toh(sg_list[i].len));
+                       }
+               } else {
+                       struct ahd_dma_seg *sg_list;
+
+                       sg_list = (struct ahd_dma_seg*)scb->sg_list;
+                       for (i = 0; i < scb->sg_count; i++) {
+                               printf("sg[%d] - Addr 0x%x%x : Length %d\n",
+                                      i,
+                                      (ahd_le32toh(sg_list[i].len) >> 24
+                                       & SG_HIGH_ADDR_BITS),
+                                      ahd_le32toh(sg_list[i].addr),
+                                      ahd_le32toh(sg_list[i].len)
+                                      & AHD_SG_LEN_MASK);
+                       }
+               }
+       }
+}
+
+/************************* Transfer Negotiation *******************************/
+/*
+ * Allocate per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static struct ahd_tmode_tstate *
+ahd_alloc_tstate(struct ahd_softc *ahd, u_int scsi_id, char channel)
+{
+       struct ahd_tmode_tstate *master_tstate;
+       struct ahd_tmode_tstate *tstate;
+       int i;
+
+       master_tstate = ahd->enabled_targets[ahd->our_id];
+       if (ahd->enabled_targets[scsi_id] != NULL
+        && ahd->enabled_targets[scsi_id] != master_tstate)
+               panic("%s: ahd_alloc_tstate - Target already allocated",
+                     ahd_name(ahd));
+       tstate = malloc(sizeof(*tstate), M_DEVBUF, M_NOWAIT);
+       if (tstate == NULL)
+               return (NULL);
+
+       /*
+        * If we have allocated a master tstate, copy user settings from
+        * the master tstate (taken from SRAM or the EEPROM) for this
+        * channel, but reset our current and goal settings to async/narrow
+        * until an initiator talks to us.
+        */
+       if (master_tstate != NULL) {
+               memcpy(tstate, master_tstate, sizeof(*tstate));
+               memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
+               for (i = 0; i < 16; i++) {
+                       memset(&tstate->transinfo[i].curr, 0,
+                             sizeof(tstate->transinfo[i].curr));
+                       memset(&tstate->transinfo[i].goal, 0,
+                             sizeof(tstate->transinfo[i].goal));
+               }
+       } else
+               memset(tstate, 0, sizeof(*tstate));
+       ahd->enabled_targets[scsi_id] = tstate;
+       return (tstate);
+}
+
+#ifdef AHD_TARGET_MODE
+/*
+ * Free per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static void
+ahd_free_tstate(struct ahd_softc *ahd, u_int scsi_id, char channel, int force)
+{
+       struct ahd_tmode_tstate *tstate;
+
+       /*
+        * Don't clean up our "master" tstate.
+        * It has our default user settings.
+        */
+       if (scsi_id == ahd->our_id
+        && force == FALSE)
+               return;
+
+       tstate = ahd->enabled_targets[scsi_id];
+       if (tstate != NULL)
+               free(tstate, M_DEVBUF);
+       ahd->enabled_targets[scsi_id] = NULL;
+}
+#endif
+
+/*
+ * Called when we have an active connection to a target on the bus,
+ * this function finds the nearest period to the input period limited
+ * by the capabilities of the bus connectivity of and sync settings for
+ * the target.
+ */
+void
+ahd_devlimited_syncrate(struct ahd_softc *ahd,
+                       struct ahd_initiator_tinfo *tinfo,
+                       u_int *period, u_int *ppr_options, role_t role)
+{
+       struct  ahd_transinfo *transinfo;
+       u_int   maxsync;
+
+       if ((ahd_inb(ahd, SBLKCTL) & ENAB40) != 0
+        && (ahd_inb(ahd, SSTAT2) & EXP_ACTIVE) == 0) {
+               maxsync = AHD_SYNCRATE_PACED;
+       } else {
+               maxsync = AHD_SYNCRATE_ULTRA;
+               /* Can't do DT related options on an SE bus */
+               *ppr_options &= MSG_EXT_PPR_QAS_REQ;
+       }
+       /*
+        * Never allow a value higher than our current goal
+        * period otherwise we may allow a target initiated
+        * negotiation to go above the limit as set by the
+        * user.  In the case of an initiator initiated
+        * sync negotiation, we limit based on the user
+        * setting.  This allows the system to still accept
+        * incoming negotiations even if target initiated
+        * negotiation is not performed.
+        */
+       if (role == ROLE_TARGET)
+               transinfo = &tinfo->user;
+       else 
+               transinfo = &tinfo->goal;
+       *ppr_options &= (transinfo->ppr_options|MSG_EXT_PPR_PCOMP_EN);
+       if (transinfo->period == 0) {
+               *period = 0;
+               *ppr_options = 0;
+       } else {
+               *period = MAX(*period, transinfo->period);
+               ahd_find_syncrate(ahd, period, ppr_options, maxsync);
+       }
+}
+
+/*
+ * Look up the valid period to SCSIRATE conversion in our table.
+ * Return the period and offset that should be sent to the target
+ * if this was the beginning of an SDTR.
+ */
+void
+ahd_find_syncrate(struct ahd_softc *ahd, u_int *period,
+                 u_int *ppr_options, u_int maxsync)
+{
+       /* Skip all PACED only entries if IU is not available */
+       if ((*ppr_options & MSG_EXT_PPR_IU_REQ) == 0
+        && maxsync < AHD_SYNCRATE_DT)
+               maxsync = AHD_SYNCRATE_DT;
+
+       /* Skip all DT only entries if DT is not available */
+       if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+        && maxsync < AHD_SYNCRATE_ULTRA2)
+               maxsync = AHD_SYNCRATE_ULTRA2;
+       
+       if (*period < maxsync)
+               *period = maxsync;
+
+       if ((*ppr_options & MSG_EXT_PPR_DT_REQ) != 0
+        && *period > AHD_SYNCRATE_MIN_DT)
+               *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+               
+       if (*period > AHD_SYNCRATE_MIN)
+               *period = 0;
+
+       /* Honor PPR option conformance rules. */
+       if ((*ppr_options & MSG_EXT_PPR_IU_REQ) == 0)
+               *ppr_options &= (MSG_EXT_PPR_DT_REQ|MSG_EXT_PPR_QAS_REQ);
+
+       if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0)
+               *ppr_options &= MSG_EXT_PPR_QAS_REQ;
+}
+
+/*
+ * Truncate the given synchronous offset to a value the
+ * current adapter type and syncrate are capable of.
+ */
+void
+ahd_validate_offset(struct ahd_softc *ahd,
+                   struct ahd_initiator_tinfo *tinfo,
+                   u_int period, u_int *offset, int wide,
+                   role_t role)
+{
+       u_int maxoffset;
+
+       /* Limit offset to what we can do */
+       if (period == 0)
+               maxoffset = 0;
+       else if (period <= AHD_SYNCRATE_PACED)
+               maxoffset = MAX_OFFSET_PACED;
+       else
+               maxoffset = MAX_OFFSET;
+       *offset = MIN(*offset, maxoffset);
+       if (tinfo != NULL) {
+               if (role == ROLE_TARGET)
+                       *offset = MIN(*offset, tinfo->user.offset);
+               else
+                       *offset = MIN(*offset, tinfo->goal.offset);
+       }
+}
+
+/*
+ * Truncate the given transfer width parameter to a value the
+ * current adapter type is capable of.
+ */
+void
+ahd_validate_width(struct ahd_softc *ahd, struct ahd_initiator_tinfo *tinfo,
+                  u_int *bus_width, role_t role)
+{
+       switch (*bus_width) {
+       default:
+               if (ahd->features & AHD_WIDE) {
+                       /* Respond Wide */
+                       *bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+                       break;
+               }
+               /* FALLTHROUGH */
+       case MSG_EXT_WDTR_BUS_8_BIT:
+               *bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+               break;
+       }
+       if (tinfo != NULL) {
+               if (role == ROLE_TARGET)
+                       *bus_width = MIN(tinfo->user.width, *bus_width);
+               else
+                       *bus_width = MIN(tinfo->goal.width, *bus_width);
+       }
+}
+
+/*
+ * Update the bitmask of targets for which the controller should
+ * negotiate with at the next convenient oportunity.  This currently
+ * means the next time we send the initial identify messages for
+ * a new transaction.
+ */
+int
+ahd_update_neg_request(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+                      struct ahd_tmode_tstate *tstate,
+                      struct ahd_initiator_tinfo *tinfo, int force)
+{
+       u_int auto_negotiate_orig;
+
+       auto_negotiate_orig = tstate->auto_negotiate;
+       if (tinfo->curr.period != tinfo->goal.period
+        || tinfo->curr.width != tinfo->goal.width
+        || tinfo->curr.offset != tinfo->goal.offset
+        || tinfo->curr.ppr_options != tinfo->goal.ppr_options
+        || (force
+         && (tinfo->goal.period != 0
+          || tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
+          || tinfo->goal.ppr_options != 0)))
+               tstate->auto_negotiate |= devinfo->target_mask;
+       else
+               tstate->auto_negotiate &= ~devinfo->target_mask;
+
+       return (auto_negotiate_orig != tstate->auto_negotiate);
+}
+
+/*
+ * Update the user/goal/curr tables of synchronous negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller.  In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahd_set_syncrate(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+                u_int period, u_int offset, u_int ppr_options,
+                u_int type, int paused)
+{
+       struct  ahd_initiator_tinfo *tinfo;
+       struct  ahd_tmode_tstate *tstate;
+       u_int   old_period;
+       u_int   old_offset;
+       u_int   old_ppr;
+       int     active;
+       int     update_needed;
+
+       active = (type & AHD_TRANS_ACTIVE) == AHD_TRANS_ACTIVE;
+       update_needed = 0;
+
+       if (period == 0 || offset == 0) {
+               period = 0;
+               offset = 0;
+       }
+
+       tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+                                   devinfo->target, &tstate);
+
+       if ((type & AHD_TRANS_USER) != 0) {
+               tinfo->user.period = period;
+               tinfo->user.offset = offset;
+               tinfo->user.ppr_options = ppr_options;
+       }
+
+       if ((type & AHD_TRANS_GOAL) != 0) {
+               tinfo->goal.period = period;
+               tinfo->goal.offset = offset;
+               tinfo->goal.ppr_options = ppr_options;
+       }
+
+       old_period = tinfo->curr.period;
+       old_offset = tinfo->curr.offset;
+       old_ppr    = tinfo->curr.ppr_options;
+
+       if ((type & AHD_TRANS_CUR) != 0
+        && (old_period != period
+         || old_offset != offset
+         || old_ppr != ppr_options)) {
+
+               update_needed++;
+
+               tinfo->curr.period = period;
+               tinfo->curr.offset = offset;
+               tinfo->curr.ppr_options = ppr_options;
+
+               ahd_send_async(ahd, devinfo->channel, devinfo->target,
+                              CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+               if (bootverbose) {
+                       if (offset != 0) {
+                               printf("%s: target %d synchronous with "
+                                      "period = 0x%x, offset = 0x%x%s\n",
+                                      ahd_name(ahd), devinfo->target,
+                                      period, offset,
+                                      (ppr_options & MSG_EXT_PPR_DT_REQ)
+                                      ? " (DT)" : "");
+                       } else {
+                               printf("%s: target %d using "
+                                      "asynchronous transfers\n",
+                                      ahd_name(ahd), devinfo->target);
+                       }
+               }
+       }
+       /*
+        * Always refresh the neg-table to handle the case of the
+        * sequencer setting the ENATNO bit for a MK_MESSAGE request.
+        * We will always renegotiate in that case if this is a
+        * packetized request.
+        */
+       if ((type & AHD_TRANS_CUR) != 0)
+               ahd_update_neg_table(ahd, devinfo, &tinfo->curr);
+
+       update_needed += ahd_update_neg_request(ahd, devinfo, tstate,
+                                               tinfo, /*force*/FALSE);
+
+       if (update_needed)
+               ahd_update_pending_scbs(ahd);
+}
+
+/*
+ * Update the user/goal/curr tables of wide negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller.  In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahd_set_width(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+             u_int width, u_int type, int paused)
+{
+       struct  ahd_initiator_tinfo *tinfo;
+       struct  ahd_tmode_tstate *tstate;
+       u_int   oldwidth;
+       int     active;
+       int     update_needed;
+
+       active = (type & AHD_TRANS_ACTIVE) == AHD_TRANS_ACTIVE;
+       update_needed = 0;
+       tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+                                   devinfo->target, &tstate);
+
+       if ((type & AHD_TRANS_USER) != 0)
+               tinfo->user.width = width;
+
+       if ((type & AHD_TRANS_GOAL) != 0)
+               tinfo->goal.width = width;
+
+       oldwidth = tinfo->curr.width;
+       if ((type & AHD_TRANS_CUR) != 0 && oldwidth != width) {
+
+               update_needed++;
+
+               tinfo->curr.width = width;
+               ahd_send_async(ahd, devinfo->channel, devinfo->target,
+                              CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+               if (bootverbose) {
+                       printf("%s: target %d using %dbit transfers\n",
+                              ahd_name(ahd), devinfo->target,
+                              8 * (0x01 << width));
+               }
+       }
+       if ((type & AHD_TRANS_CUR) != 0)
+               ahd_update_neg_table(ahd, devinfo, &tinfo->curr);
+
+       update_needed += ahd_update_neg_request(ahd, devinfo, tstate,
+                                               tinfo, /*force*/FALSE);
+       if (update_needed)
+               ahd_update_pending_scbs(ahd);
+}
+
+/*
+ * Update the current state of tagged queuing for a given target.
+ */
+void
+ahd_set_tags(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+            ahd_queue_alg alg)
+{
+       ahd_platform_set_tags(ahd, devinfo, alg);
+       ahd_send_async(ahd, devinfo->channel, devinfo->target,
+                      devinfo->lun, AC_TRANSFER_NEG, &alg);
+}
+
+static void
+ahd_update_neg_table(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+                    struct ahd_transinfo *tinfo)
+{
+       ahd_mode_state  saved_modes;
+       u_int           period;
+       u_int           ppr_opts;
+       u_int           con_opts;
+       u_int           offset;
+       u_int           precomp;
+
+       saved_modes = ahd_save_modes(ahd);
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+       ahd_outb(ahd, NEGOADDR, devinfo->target);
+       period = tinfo->period;
+       offset = tinfo->offset;
+       precomp = 0;
+       if (period == 0)
+               period = AHD_SYNCRATE_ASYNC;
+       if (period == AHD_SYNCRATE_160) {
+               period = AHD_SYNCRATE_REVA_160;
+               precomp = 0;
+               if ((ahd->flags & AHD_CPQ_BOARD) == 0)
+                       precomp |= AHD_PRECOMP_FASTSLEW;
+               if ((tinfo->ppr_options & MSG_EXT_PPR_PCOMP_EN) != 0)
+                       precomp |= AHD_PRECOMP_CUTBACK_29;
+       }
+       ahd_outb(ahd, ANNEXCOL, AHD_ANNEXCOL_PRECOMP);
+       ahd_outb(ahd, ANNEXDAT, precomp);
+
+       ahd_outb(ahd, NEGPERIOD, period);
+       ppr_opts = tinfo->ppr_options
+                & (MSG_EXT_PPR_QAS_REQ|MSG_EXT_PPR_DT_REQ|MSG_EXT_PPR_IU_REQ);
+       /*
+        * When the SPI4 spec was finalized, PACE transfers
+        * was not made a configurable option in the PPR message.
+        * Instead it is assumed to be enabled for any
+        * syncrate faster than 80MHz.  Nevertheless, Harpoon
+        * allows this to be configurable.
+        *
+        * Harpoon also assumes at most 2 data bytes per negotiated
+        * REQ/ACK offset.  Paced transfers take 4, so we must
+        * adjust our offset.
+        */
+       if (period <= AHD_SYNCRATE_PACED) {
+               ppr_opts |= PPROPT_PACE;
+               offset *= 2;
+       }
+       ahd_outb(ahd, NEGPPROPTS, ppr_opts);
+       ahd_outb(ahd, NEGOFFSET, offset);
+
+       con_opts = 0;
+       if (tinfo->width == MSG_EXT_WDTR_BUS_16_BIT)
+               con_opts |= WIDEXFER;
+
+       /*
+        * During packetized transfers, the target will
+        * give us the oportunity to send command packets
+        * without us asserting attention.
+        */
+       if ((tinfo->ppr_options & MSG_EXT_PPR_IU_REQ) == 0)
+               con_opts |= ENAUTOATNO;
+       ahd_outb(ahd, NEGCONOPTS, con_opts);
+       ahd_restore_modes(ahd, saved_modes);
+}
+
+/*
+ * When the transfer settings for a connection change, update any
+ * in-transit SCBs to contain the new data so the hardware will
+ * be set correctly during future (re)selections.
+ */
+static void
+ahd_update_pending_scbs(struct ahd_softc *ahd)
+{
+       struct          scb *pending_scb;
+       int             pending_scb_count;
+       int             i;
+       int             paused;
+       u_int           saved_scbptr;
+       ahd_mode_state  saved_modes;
+
+       /*
+        * Traverse the pending SCB list and ensure that all of the
+        * SCBs there have the proper settings.
+        */
+       pending_scb_count = 0;
+       LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) {
+               struct ahd_devinfo devinfo;
+               struct hardware_scb *pending_hscb;
+               struct ahd_initiator_tinfo *tinfo;
+               struct ahd_tmode_tstate *tstate;
+
+               ahd_scb_devinfo(ahd, &devinfo, pending_scb);
+               tinfo = ahd_fetch_transinfo(ahd, devinfo.channel,
+                                           devinfo.our_scsiid,
+                                           devinfo.target, &tstate);
+               pending_hscb = pending_scb->hscb;
+               if ((tstate->auto_negotiate & devinfo.target_mask) == 0
+                && (pending_scb->flags & SCB_AUTO_NEGOTIATE) != 0) {
+                       pending_scb->flags &= ~SCB_AUTO_NEGOTIATE;
+                       pending_hscb->control &= ~MK_MESSAGE;
+               }
+               ahd_sync_scb(ahd, pending_scb,
+                            BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+               pending_scb_count++;
+       }
+
+       if (pending_scb_count == 0)
+               return;
+
+       if (ahd_is_paused(ahd)) {
+               paused = 1;
+       } else {
+               paused = 0;
+               ahd_pause(ahd);
+       }
+
+       saved_modes = ahd_save_modes(ahd);
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       saved_scbptr = ahd_get_scbptr(ahd);
+       /* Ensure that the hscbs down on the card match the new information */
+       for (i = 0; i < ahd->scb_data.maxhscbs; i++) {
+               struct  hardware_scb *pending_hscb;
+               u_int   control;
+               u_int   scb_tag;
+
+               ahd_set_scbptr(ahd, i);
+               scb_tag = i;
+               pending_scb = ahd_lookup_scb(ahd, scb_tag);
+               if (pending_scb == NULL)
+                       continue;
+
+               pending_hscb = pending_scb->hscb;
+               control = ahd_inb_scbram(ahd, SCB_CONTROL);
+               control &= ~MK_MESSAGE;
+               control |= pending_hscb->control & MK_MESSAGE;
+               ahd_outb(ahd, SCB_CONTROL, control);
+       }
+       ahd_set_scbptr(ahd,saved_scbptr);
+       ahd_restore_modes(ahd, saved_modes);
+
+       if (paused == 0)
+               ahd_unpause(ahd);
+}
+
+/**************************** Pathing Information *****************************/
+static void
+ahd_fetch_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+       ahd_mode_state  saved_modes;
+       u_int           saved_scsiid;
+       role_t          role;
+       int             our_id;
+
+       saved_modes = ahd_save_modes(ahd);
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+       if (ahd_inb(ahd, SSTAT0) & TARGET)
+               role = ROLE_TARGET;
+       else
+               role = ROLE_INITIATOR;
+
+       if (role == ROLE_TARGET
+        && (ahd_inb(ahd, SEQ_FLAGS) & CMDPHASE_PENDING) != 0) {
+               /* We were selected, so pull our id from TARGIDIN */
+               our_id = ahd_inb(ahd, TARGIDIN) & OID;
+       } else if (role == ROLE_TARGET)
+               our_id = ahd_inb(ahd, TOWNID);
+       else
+               our_id = ahd_inb(ahd, IOWNID);
+
+       saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
+       ahd_compile_devinfo(devinfo,
+                           our_id,
+                           SCSIID_TARGET(ahd, saved_scsiid),
+                           ahd_inb(ahd, SAVED_LUN),
+                           SCSIID_CHANNEL(ahd, saved_scsiid),
+                           role);
+       ahd_restore_modes(ahd, saved_modes);
+}
+
+struct ahd_phase_table_entry*
+ahd_lookup_phase_entry(int phase)
+{
+       struct ahd_phase_table_entry *entry;
+       struct ahd_phase_table_entry *last_entry;
+
+       /*
+        * num_phases doesn't include the default entry which
+        * will be returned if the phase doesn't match.
+        */
+       last_entry = &ahd_phase_table[num_phases];
+       for (entry = ahd_phase_table; entry < last_entry; entry++) {
+               if (phase == entry->phase)
+                       break;
+       }
+       return (entry);
+}
+
+void
+ahd_compile_devinfo(struct ahd_devinfo *devinfo, u_int our_id, u_int target,
+                   u_int lun, char channel, role_t role)
+{
+       devinfo->our_scsiid = our_id;
+       devinfo->target = target;
+       devinfo->lun = lun;
+       devinfo->target_offset = target;
+       devinfo->channel = channel;
+       devinfo->role = role;
+       if (channel == 'B')
+               devinfo->target_offset += 8;
+       devinfo->target_mask = (0x01 << devinfo->target_offset);
+}
+
+static void
+ahd_scb_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+               struct scb *scb)
+{
+       role_t  role;
+       int     our_id;
+
+       our_id = SCSIID_OUR_ID(scb->hscb->scsiid);
+       role = ROLE_INITIATOR;
+       if ((scb->hscb->control & TARGET_SCB) != 0)
+               role = ROLE_TARGET;
+       ahd_compile_devinfo(devinfo, our_id, SCB_GET_TARGET(ahd, scb),
+                           SCB_GET_LUN(scb), SCB_GET_CHANNEL(ahd, scb), role);
+}
+
+
+/************************ Message Phase Processing ****************************/
+/*
+ * When an initiator transaction with the MK_MESSAGE flag either reconnects
+ * or enters the initial message out phase, we are interrupted.  Fill our
+ * outgoing message buffer with the appropriate message and beging handing
+ * the message phase(s) manually.
+ */
+static void
+ahd_setup_initiator_msgout(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+                          struct scb *scb)
+{
+       /*
+        * To facilitate adding multiple messages together,
+        * each routine should increment the index and len
+        * variables instead of setting them explicitly.
+        */
+       ahd->msgout_index = 0;
+       ahd->msgout_len = 0;
+
+       if (ahd_currently_packetized(ahd))
+               ahd->msg_flags |= MSG_FLAG_PACKETIZED;
+
+       if (ahd->send_msg_perror
+        && ahd_inb(ahd, MSG_OUT) == HOST_MSG) {
+               ahd->msgout_buf[ahd->msgout_index++] = ahd->send_msg_perror;
+               ahd->msgout_len++;
+               ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+               printf("Setting up for Parity Error delivery\n");
+               return;
+       } else if (scb == NULL) {
+               printf("%s: WARNING. No pending message for "
+                      "I_T msgin.  Issuing NO-OP\n", ahd_name(ahd));
+               ahd->msgout_buf[ahd->msgout_index++] = MSG_NOOP;
+               ahd->msgout_len++;
+               ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+               return;
+       }
+
+       if ((scb->flags & SCB_DEVICE_RESET) == 0
+        && (scb->flags & SCB_PACKETIZED) == 0
+        && ahd_inb(ahd, MSG_OUT) == MSG_IDENTIFYFLAG) {
+               u_int identify_msg;
+
+               identify_msg = MSG_IDENTIFYFLAG | SCB_GET_LUN(scb);
+               if ((scb->hscb->control & DISCENB) != 0)
+                       identify_msg |= MSG_IDENTIFY_DISCFLAG;
+               ahd->msgout_buf[ahd->msgout_index++] = identify_msg;
+               ahd->msgout_len++;
+
+               if ((scb->hscb->control & TAG_ENB) != 0) {
+                       ahd->msgout_buf[ahd->msgout_index++] =
+                           scb->hscb->control & (TAG_ENB|SCB_TAG_TYPE);
+                       ahd->msgout_buf[ahd->msgout_index++] = SCB_GET_TAG(scb);
+                       ahd->msgout_len += 2;
+               }
+       }
+
+       if (scb->flags & SCB_DEVICE_RESET) {
+               ahd->msgout_buf[ahd->msgout_index++] = MSG_BUS_DEV_RESET;
+               ahd->msgout_len++;
+               ahd_print_path(ahd, scb);
+               printf("Bus Device Reset Message Sent\n");
+               /*
+                * Clear our selection hardware in advance of
+                * the busfree.  We may have an entry in the waiting
+                * Q for this target, and we don't want to go about
+                * selecting while we handle the busfree and blow it
+                * away.
+                */
+               ahd_outb(ahd, SCSISEQ0, 0);
+       } else if ((scb->flags & SCB_ABORT) != 0) {
+
+               if ((scb->hscb->control & TAG_ENB) != 0) {
+                       ahd->msgout_buf[ahd->msgout_index++] = MSG_ABORT_TAG;
+               } else {
+                       ahd->msgout_buf[ahd->msgout_index++] = MSG_ABORT;
+               }
+               ahd->msgout_len++;
+               ahd_print_path(ahd, scb);
+               printf("Abort%s Message Sent\n",
+                      (scb->hscb->control & TAG_ENB) != 0 ? " Tag" : "");
+               /*
+                * Clear our selection hardware in advance of
+                * the busfree.  We may have an entry in the waiting
+                * Q for this target, and we don't want to go about
+                * selecting while we handle the busfree and blow it
+                * away.
+                */
+               ahd_outb(ahd, SCSISEQ0, 0);
+       } else if ((scb->flags & (SCB_AUTO_NEGOTIATE|SCB_NEGOTIATE)) != 0) {
+               ahd_build_transfer_msg(ahd, devinfo);
+               /*
+                * Clear our selection hardware in advance of potential
+                * PPR IU status change busfree.  We may have an entry in
+                * the waiting Q for this target, and we don't want to go
+                * about selecting while we handle the busfree and blow
+                * it away.
+                */
+               ahd_outb(ahd, SCSISEQ0, 0);
+       } else {
+               printf("ahd_intr: AWAITING_MSG for an SCB that "
+                      "does not have a waiting message\n");
+               printf("SCSIID = %x, target_mask = %x\n", scb->hscb->scsiid,
+                      devinfo->target_mask);
+               panic("SCB = %d, SCB Control = %x, MSG_OUT = %x "
+                     "SCB flags = %x", SCB_GET_TAG(scb), scb->hscb->control,
+                     ahd_inb(ahd, MSG_OUT), scb->flags);
+       }
+
+       /*
+        * Clear the MK_MESSAGE flag from the SCB so we aren't
+        * asked to send this message again.
+        */
+       ahd_outb(ahd, SCB_CONTROL,
+                ahd_inb_scbram(ahd, SCB_CONTROL) & ~MK_MESSAGE);
+       scb->hscb->control &= ~MK_MESSAGE;
+       ahd->msgout_index = 0;
+       ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+}
+
+/*
+ * Build an appropriate transfer negotiation message for the
+ * currently active target.
+ */
+static void
+ahd_build_transfer_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+       /*
+        * We need to initiate transfer negotiations.
+        * If our current and goal settings are identical,
+        * we want to renegotiate due to a check condition.
+        */
+       struct  ahd_initiator_tinfo *tinfo;
+       struct  ahd_tmode_tstate *tstate;
+       int     dowide;
+       int     dosync;
+       int     doppr;
+       int     use_ppr;
+       u_int   period;
+       u_int   ppr_options;
+       u_int   offset;
+
+       tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+                                   devinfo->target, &tstate);
+       /*
+        * Filter our period based on the current connection.
+        * If we can't perform DT transfers on this segment (not in LVD
+        * mode for instance), then our decision to issue a PPR message
+        * may change.
+        */
+       period = tinfo->goal.period;
+       ppr_options = tinfo->goal.ppr_options;
+       /* Target initiated PPR is not allowed in the SCSI spec */
+       if (devinfo->role == ROLE_TARGET)
+               ppr_options = 0;
+       ahd_devlimited_syncrate(ahd, tinfo, &period,
+                               &ppr_options, devinfo->role);
+       dowide = tinfo->curr.width != tinfo->goal.width;
+       dosync = tinfo->curr.period != period;
+       doppr = tinfo->curr.ppr_options != ppr_options;
+
+       if (!dowide && !dosync && !doppr) {
+               dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
+               dosync = tinfo->goal.period != 0;
+               doppr = tinfo->goal.ppr_options != 0;
+       }
+
+       if (!dowide && !dosync && !doppr) {
+               panic("ahd_intr: AWAITING_MSG for negotiation, "
+                     "but no negotiation needed\n");   
+       }
+
+       use_ppr = (tinfo->curr.transport_version >= 3) || doppr;
+       /* Target initiated PPR is not allowed in the SCSI spec */
+       if (devinfo->role == ROLE_TARGET)
+               use_ppr = 0;
+
+       /*
+        * Both the PPR message and SDTR message require the
+        * goal syncrate to be limited to what the target device
+        * is capable of handling (based on whether an LVD->SE
+        * expander is on the bus), so combine these two cases.
+        * Regardless, guarantee that if we are using WDTR and SDTR
+        * messages that WDTR comes first.
+        */
+       if (use_ppr || (dosync && !dowide)) {
+
+               offset = tinfo->goal.offset;
+               ahd_validate_offset(ahd, tinfo, period, &offset,
+                                   use_ppr ? tinfo->goal.width
+                                           : tinfo->curr.width,
+                                   devinfo->role);
+               if (use_ppr) {
+                       ahd_construct_ppr(ahd, devinfo, period, offset,
+                                         tinfo->goal.width, ppr_options);
+               } else {
+                       ahd_construct_sdtr(ahd, devinfo, period, offset);
+               }
+       } else {
+               ahd_construct_wdtr(ahd, devinfo, tinfo->goal.width);
+       }
+}
+
+/*
+ * Build a synchronous negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahd_construct_sdtr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+                  u_int period, u_int offset)
+{
+       ahd->msgout_buf[ahd->msgout_index++] = MSG_EXTENDED;
+       ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_SDTR_LEN;
+       ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_SDTR;
+       ahd->msgout_buf[ahd->msgout_index++] = period;
+       ahd->msgout_buf[ahd->msgout_index++] = offset;
+       ahd->msgout_len += 5;
+       if (bootverbose) {
+               printf("(%s:%c:%d:%d): Sending SDTR period %x, offset %x\n",
+                      ahd_name(ahd), devinfo->channel, devinfo->target,
+                      devinfo->lun, period, offset);
+       }
+}
+
+/*
+ * Build a wide negotiateion message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahd_construct_wdtr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+                  u_int bus_width)
+{
+       ahd->msgout_buf[ahd->msgout_index++] = MSG_EXTENDED;
+       ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_WDTR_LEN;
+       ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_WDTR;
+       ahd->msgout_buf[ahd->msgout_index++] = bus_width;
+       ahd->msgout_len += 4;
+       if (bootverbose) {
+               printf("(%s:%c:%d:%d): Sending WDTR %x\n",
+                      ahd_name(ahd), devinfo->channel, devinfo->target,
+                      devinfo->lun, bus_width);
+       }
+}
+
+/*
+ * Build a parallel protocol request message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahd_construct_ppr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+                 u_int period, u_int offset, u_int bus_width,
+                 u_int ppr_options)
+{
+       /*
+        * Always request precompensation from
+        * the other target if we are running
+        * at paced syncrates.
+        */
+       if (period <= AHD_SYNCRATE_PACED)
+               ppr_options |= MSG_EXT_PPR_PCOMP_EN;
+       ahd->msgout_buf[ahd->msgout_index++] = MSG_EXTENDED;
+       ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_PPR_LEN;
+       ahd->msgout_buf[ahd->msgout_index++] = MSG_EXT_PPR;
+       ahd->msgout_buf[ahd->msgout_index++] = period;
+       ahd->msgout_buf[ahd->msgout_index++] = 0;
+       ahd->msgout_buf[ahd->msgout_index++] = offset;
+       ahd->msgout_buf[ahd->msgout_index++] = bus_width;
+       ahd->msgout_buf[ahd->msgout_index++] = ppr_options;
+       ahd->msgout_len += 8;
+       if (bootverbose) {
+               printf("(%s:%c:%d:%d): Sending PPR bus_width %x, period %x, "
+                      "offset %x, ppr_options %x\n", ahd_name(ahd),
+                      devinfo->channel, devinfo->target, devinfo->lun,
+                      bus_width, period, offset, ppr_options);
+       }
+}
+
+/*
+ * Clear any active message state.
+ */
+static void
+ahd_clear_msg_state(struct ahd_softc *ahd)
+{
+       ahd_mode_state saved_modes;
+
+       saved_modes = ahd_save_modes(ahd);
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       ahd->send_msg_perror = 0;
+       ahd->msg_flags = MSG_FLAG_NONE;
+       ahd->msgout_len = 0;
+       ahd->msgin_index = 0;
+       ahd->msg_type = MSG_TYPE_NONE;
+       if ((ahd_inb(ahd, SCSISIGO) & ATNO) != 0) {
+               /*
+                * The target didn't care to respond to our
+                * message request, so clear ATN.
+                */
+               ahd_outb(ahd, CLRSINT1, CLRATNO);
+       }
+       ahd_outb(ahd, MSG_OUT, MSG_NOOP);
+       ahd_outb(ahd, SEQ_FLAGS2,
+                ahd_inb(ahd, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
+       ahd_restore_modes(ahd, saved_modes);
+}
+
+/*
+ * Manual message loop handler.
+ */
+static void
+ahd_handle_message_phase(struct ahd_softc *ahd)
+{ 
+       struct  ahd_devinfo devinfo;
+       u_int   bus_phase;
+       int     end_session;
+
+       ahd_fetch_devinfo(ahd, &devinfo);
+       end_session = FALSE;
+       bus_phase = ahd_inb(ahd, LASTPHASE);
+
+       if ((ahd_inb(ahd, LQISTAT2) & LQIPHASE_OUTPKT) != 0) {
+               printf("LQIRETRY for LQIPHASE_OUTPKT\n");
+               ahd_outb(ahd, LQCTL2, LQIRETRY);
+       }
+reswitch:
+       switch (ahd->msg_type) {
+       case MSG_TYPE_INITIATOR_MSGOUT:
+       {
+               int lastbyte;
+               int phasemis;
+               int msgdone;
+
+               if (ahd->msgout_len == 0 && ahd->send_msg_perror == 0)
+                       panic("HOST_MSG_LOOP interrupt with no active message");
+
+#if AHD_DEBUG
+               if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+                       printf("%s: INITIATOR_MSG_OUT", ahd_name(ahd));
+#endif
+               phasemis = bus_phase != P_MESGOUT;
+               if (phasemis) {
+#if AHD_DEBUG
+                       if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+                               printf(" PHASEMIS %s - %s\n",
+                                      ahd_lookup_phase_entry(bus_phase)
+                                                            ->phasemsg,
+                                      ahd_lookup_phase_entry(ahd_inb(ahd, SCSISIGI) & PHASE_MASK)
+                                                            ->phasemsg);
+                       }
+#endif
+                       if (bus_phase == P_MESGIN) {
+                               /*
+                                * Change gears and see if
+                                * this messages is of interest to
+                                * us or should be passed back to
+                                * the sequencer.
+                                */
+                               ahd_outb(ahd, CLRSINT1, CLRATNO);
+                               ahd->send_msg_perror = 0;
+                               ahd->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+                               ahd->msgin_index = 0;
+                               goto reswitch;
+                       }
+                       end_session = TRUE;
+                       break;
+               }
+
+               if (ahd->send_msg_perror) {
+                       ahd_outb(ahd, CLRSINT1, CLRATNO);
+                       ahd_outb(ahd, CLRSINT1, CLRREQINIT);
+#if AHD_DEBUG
+                       if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+                               printf(" byte 0x%x\n", ahd->send_msg_perror);
+#endif
+                       /*
+                        * If we are notifying the target of a CRC error
+                        * during packetized operations, the target is
+                        * within its rights to acknowledge our message
+                        * with a busfree.
+                        */
+                       if ((ahd->msg_flags & MSG_FLAG_PACKETIZED) != 0
+                        && ahd->send_msg_perror == MSG_INITIATOR_DET_ERR)
+                               ahd->msg_flags |= MSG_FLAG_EXPECT_IDE_BUSFREE;
+
+                       ahd_outb(ahd, RETURN_2, ahd->send_msg_perror);
+                       ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_WRITE);
+                       break;
+               }
+
+               msgdone = ahd->msgout_index == ahd->msgout_len;
+               if (msgdone) {
+                       /*
+                        * The target has requested a retry.
+                        * Re-assert ATN, reset our message index to
+                        * 0, and try again.
+                        */
+                       ahd->msgout_index = 0;
+                       ahd_assert_atn(ahd);
+               }
+
+               lastbyte = ahd->msgout_index == (ahd->msgout_len - 1);
+               if (lastbyte) {
+                       /* Last byte is signified by dropping ATN */
+                       ahd_outb(ahd, CLRSINT1, CLRATNO);
+               }
+
+               /*
+                * Clear our interrupt status and present
+                * the next byte on the bus.
+                */
+               ahd_outb(ahd, CLRSINT1, CLRREQINIT);
+#if AHD_DEBUG
+               if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+                       printf(" byte 0x%x\n",
+                              ahd->msgout_buf[ahd->msgout_index]);
+#endif
+               ahd_outb(ahd, RETURN_2, ahd->msgout_buf[ahd->msgout_index++]);
+               ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_WRITE);
+               break;
+       }
+       case MSG_TYPE_INITIATOR_MSGIN:
+       {
+               int phasemis;
+               int message_done;
+
+#if AHD_DEBUG
+               if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+                       printf("%s: INITIATOR_MSG_IN", ahd_name(ahd));
+#endif
+               phasemis = bus_phase != P_MESGIN;
+               if (phasemis) {
+#if AHD_DEBUG
+                       if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
+                               printf(" PHASEMIS %s\n",
+                                      ahd_lookup_phase_entry(bus_phase)
+                                                            ->phasemsg);
+                       }
+#endif
+                       ahd->msgin_index = 0;
+                       if (bus_phase == P_MESGOUT
+                        && (ahd->send_msg_perror != 0
+                         || (ahd->msgout_len != 0
+                          && ahd->msgout_index == 0))) {
+                               ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+                               goto reswitch;
+                       }
+                       end_session = TRUE;
+                       break;
+               }
+
+               /* Pull the byte in without acking it */
+               ahd->msgin_buf[ahd->msgin_index] = ahd_inb(ahd, SCSIBUS);
+#if AHD_DEBUG
+               if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+                       printf(" byte 0x%x\n",
+                              ahd->msgin_buf[ahd->msgin_index]);
+#endif
+
+               message_done = ahd_parse_msg(ahd, &devinfo);
+
+               if (message_done) {
+                       /*
+                        * Clear our incoming message buffer in case there
+                        * is another message following this one.
+                        */
+                       ahd->msgin_index = 0;
+
+                       /*
+                        * If this message illicited a response,
+                        * assert ATN so the target takes us to the
+                        * message out phase.
+                        */
+                       if (ahd->msgout_len != 0)
+                               ahd_assert_atn(ahd);
+               } else 
+                       ahd->msgin_index++;
+
+               if (message_done == MSGLOOP_TERMINATED) {
+                       end_session = TRUE;
+               } else {
+                       /* Ack the byte */
+                       ahd_outb(ahd, CLRSINT1, CLRREQINIT);
+                       ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_READ);
+               }
+               break;
+       }
+       case MSG_TYPE_TARGET_MSGIN:
+       {
+               int msgdone;
+               int msgout_request;
+
+               /*
+                * By default, the message loop will continue.
+                */
+               ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_TARG);
+
+               if (ahd->msgout_len == 0)
+                       panic("Target MSGIN with no active message");
+
+               /*
+                * If we interrupted a mesgout session, the initiator
+                * will not know this until our first REQ.  So, we
+                * only honor mesgout requests after we've sent our
+                * first byte.
+                */
+               if ((ahd_inb(ahd, SCSISIGI) & ATNI) != 0
+                && ahd->msgout_index > 0)
+                       msgout_request = TRUE;
+               else
+                       msgout_request = FALSE;
+
+               if (msgout_request) {
+
+                       /*
+                        * Change gears and see if
+                        * this messages is of interest to
+                        * us or should be passed back to
+                        * the sequencer.
+                        */
+                       ahd->msg_type = MSG_TYPE_TARGET_MSGOUT;
+                       ahd_outb(ahd, SCSISIGO, P_MESGOUT | BSYO);
+                       ahd->msgin_index = 0;
+                       /* Dummy read to REQ for first byte */
+                       ahd_inb(ahd, SCSIDAT);
+                       ahd_outb(ahd, SXFRCTL0,
+                                ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+                       break;
+               }
+
+               msgdone = ahd->msgout_index == ahd->msgout_len;
+               if (msgdone) {
+                       ahd_outb(ahd, SXFRCTL0,
+                                ahd_inb(ahd, SXFRCTL0) & ~SPIOEN);
+                       end_session = TRUE;
+                       break;
+               }
+
+               /*
+                * Present the next byte on the bus.
+                */
+               ahd_outb(ahd, SXFRCTL0, ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+               ahd_outb(ahd, SCSIDAT, ahd->msgout_buf[ahd->msgout_index++]);
+               break;
+       }
+       case MSG_TYPE_TARGET_MSGOUT:
+       {
+               int lastbyte;
+               int msgdone;
+
+               /*
+                * By default, the message loop will continue.
+                */
+               ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_TARG);
+
+               /*
+                * The initiator signals that this is
+                * the last byte by dropping ATN.
+                */
+               lastbyte = (ahd_inb(ahd, SCSISIGI) & ATNI) == 0;
+
+               /*
+                * Read the latched byte, but turn off SPIOEN first
+                * so that we don't inadvertently cause a REQ for the
+                * next byte.
+                */
+               ahd_outb(ahd, SXFRCTL0, ahd_inb(ahd, SXFRCTL0) & ~SPIOEN);
+               ahd->msgin_buf[ahd->msgin_index] = ahd_inb(ahd, SCSIDAT);
+               msgdone = ahd_parse_msg(ahd, &devinfo);
+               if (msgdone == MSGLOOP_TERMINATED) {
+                       /*
+                        * The message is *really* done in that it caused
+                        * us to go to bus free.  The sequencer has already
+                        * been reset at this point, so pull the ejection
+                        * handle.
+                        */
+                       return;
+               }
+               
+               ahd->msgin_index++;
+
+               /*
+                * XXX Read spec about initiator dropping ATN too soon
+                *     and use msgdone to detect it.
+                */
+               if (msgdone == MSGLOOP_MSGCOMPLETE) {
+                       ahd->msgin_index = 0;
+
+                       /*
+                        * If this message illicited a response, transition
+                        * to the Message in phase and send it.
+                        */
+                       if (ahd->msgout_len != 0) {
+                               ahd_outb(ahd, SCSISIGO, P_MESGIN | BSYO);
+                               ahd_outb(ahd, SXFRCTL0,
+                                        ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+                               ahd->msg_type = MSG_TYPE_TARGET_MSGIN;
+                               ahd->msgin_index = 0;
+                               break;
+                       }
+               }
+
+               if (lastbyte)
+                       end_session = TRUE;
+               else {
+                       /* Ask for the next byte. */
+                       ahd_outb(ahd, SXFRCTL0,
+                                ahd_inb(ahd, SXFRCTL0) | SPIOEN);
+               }
+
+               break;
+       }
+       default:
+               panic("Unknown REQINIT message type");
+       }
+
+       if (end_session) {
+               if ((ahd->msg_flags & MSG_FLAG_PACKETIZED) != 0) {
+                       printf("%s: Returning to Idle Loop\n",
+                              ahd_name(ahd));
+                       ahd_outb(ahd, LASTPHASE, P_BUSFREE);
+                       ahd_clear_msg_state(ahd);
+                       ahd_outb(ahd, SEQCTL0, FASTMODE|SEQRESET);
+               } else {
+                       ahd_clear_msg_state(ahd);
+                       ahd_outb(ahd, RETURN_1, EXIT_MSG_LOOP);
+               }
+       }
+}
+
+/*
+ * See if we sent a particular extended message to the target.
+ * If "full" is true, return true only if the target saw the full
+ * message.  If "full" is false, return true if the target saw at
+ * least the first byte of the message.
+ */
+static int
+ahd_sent_msg(struct ahd_softc *ahd, ahd_msgtype type, u_int msgval, int full)
+{
+       int found;
+       u_int index;
+
+       found = FALSE;
+       index = 0;
+
+       while (index < ahd->msgout_len) {
+               if (ahd->msgout_buf[index] == MSG_EXTENDED) {
+                       u_int end_index;
+
+                       end_index = index + 1 + ahd->msgout_buf[index + 1];
+                       if (ahd->msgout_buf[index+2] == msgval
+                        && type == AHDMSG_EXT) {
+
+                               if (full) {
+                                       if (ahd->msgout_index > end_index)
+                                               found = TRUE;
+                               } else if (ahd->msgout_index > index)
+                                       found = TRUE;
+                       }
+                       index = end_index;
+               } else if (ahd->msgout_buf[index] >= MSG_SIMPLE_TASK
+                       && ahd->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
+
+                       /* Skip tag type and tag id or residue param*/
+                       index += 2;
+               } else {
+                       /* Single byte message */
+                       if (type == AHDMSG_1B
+                        && ahd->msgout_buf[index] == msgval
+                        && ahd->msgout_index > index)
+                               found = TRUE;
+                       index++;
+               }
+
+               if (found)
+                       break;
+       }
+       return (found);
+}
+
+/*
+ * Wait for a complete incoming message, parse it, and respond accordingly.
+ */
+static int
+ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+       struct  ahd_initiator_tinfo *tinfo;
+       struct  ahd_tmode_tstate *tstate;
+       int     reject;
+       int     done;
+       int     response;
+
+       done = MSGLOOP_IN_PROG;
+       response = FALSE;
+       reject = FALSE;
+       tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
+                                   devinfo->target, &tstate);
+
+       /*
+        * Parse as much of the message as is availible,
+        * rejecting it if we don't support it.  When
+        * the entire message is availible and has been
+        * handled, return MSGLOOP_MSGCOMPLETE, indicating
+        * that we have parsed an entire message.
+        *
+        * In the case of extended messages, we accept the length
+        * byte outright and perform more checking once we know the
+        * extended message type.
+        */
+       switch (ahd->msgin_buf[0]) {
+       case MSG_DISCONNECT:
+       case MSG_SAVEDATAPOINTER:
+       case MSG_CMDCOMPLETE:
+       case MSG_RESTOREPOINTERS:
+       case MSG_IGN_WIDE_RESIDUE:
+               /*
+                * End our message loop as these are messages
+                * the sequencer handles on its own.
+                */
+               done = MSGLOOP_TERMINATED;
+               break;
+       case MSG_MESSAGE_REJECT:
+               response = ahd_handle_msg_reject(ahd, devinfo);
+               /* FALLTHROUGH */
+       case MSG_NOOP:
+               done = MSGLOOP_MSGCOMPLETE;
+               break;
+       case MSG_EXTENDED:
+       {
+               /* Wait for enough of the message to begin validation */
+               if (ahd->msgin_index < 2)
+                       break;
+               switch (ahd->msgin_buf[2]) {
+               case MSG_EXT_SDTR:
+               {
+                       u_int    period;
+                       u_int    ppr_options;
+                       u_int    offset;
+                       u_int    saved_offset;
+                       
+                       if (ahd->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
+                               reject = TRUE;
+                               break;
+                       }
+
+                       /*
+                        * Wait until we have both args before validating
+                        * and acting on this message.
+                        *
+                        * Add one to MSG_EXT_SDTR_LEN to account for
+                        * the extended message preamble.
+                        */
+                       if (ahd->msgin_index < (MSG_EXT_SDTR_LEN + 1))
+                               break;
+
+                       period = ahd->msgin_buf[3];
+                       ppr_options = 0;
+                       saved_offset = offset = ahd->msgin_buf[4];
+                       ahd_devlimited_syncrate(ahd, tinfo, &period,
+                                               &ppr_options, devinfo->role);
+                       ahd_validate_offset(ahd, tinfo, period, &offset,
+                                           tinfo->curr.width, devinfo->role);
+                       if (bootverbose) {
+                               printf("(%s:%c:%d:%d): Received "
+                                      "SDTR period %x, offset %x\n\t"
+                                      "Filtered to period %x, offset %x\n",
+                                      ahd_name(ahd), devinfo->channel,
+                                      devinfo->target, devinfo->lun,
+                                      ahd->msgin_buf[3], saved_offset,
+                                      period, offset);
+                       }
+                       ahd_set_syncrate(ahd, devinfo, period,
+                                        offset, ppr_options,
+                                        AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+                                        /*paused*/TRUE);
+
+                       /*
+                        * See if we initiated Sync Negotiation
+                        * and didn't have to fall down to async
+                        * transfers.
+                        */
+                       if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, TRUE)) {
+                               /* We started it */
+                               if (saved_offset != offset) {
+                                       /* Went too low - force async */
+                                       reject = TRUE;
+                               }
+                       } else {
+                               /*
+                                * Send our own SDTR in reply
+                                */
+                               if (bootverbose
+                                && devinfo->role == ROLE_INITIATOR) {
+                                       printf("(%s:%c:%d:%d): Target "
+                                              "Initiated SDTR\n",
+                                              ahd_name(ahd), devinfo->channel,
+                                              devinfo->target, devinfo->lun);
+                               }
+                               ahd->msgout_index = 0;
+                               ahd->msgout_len = 0;
+                               ahd_construct_sdtr(ahd, devinfo,
+                                                  period, offset);
+                               ahd->msgout_index = 0;
+                               response = TRUE;
+                       }
+                       done = MSGLOOP_MSGCOMPLETE;
+                       break;
+               }
+               case MSG_EXT_WDTR:
+               {
+                       u_int bus_width;
+                       u_int saved_width;
+                       u_int sending_reply;
+
+                       sending_reply = FALSE;
+                       if (ahd->msgin_buf[1] != MSG_EXT_WDTR_LEN) {
+                               reject = TRUE;
+                               break;
+                       }
+
+                       /*
+                        * Wait until we have our arg before validating
+                        * and acting on this message.
+                        *
+                        * Add one to MSG_EXT_WDTR_LEN to account for
+                        * the extended message preamble.
+                        */
+                       if (ahd->msgin_index < (MSG_EXT_WDTR_LEN + 1))
+                               break;
+
+                       bus_width = ahd->msgin_buf[3];
+                       saved_width = bus_width;
+                       ahd_validate_width(ahd, tinfo, &bus_width,
+                                          devinfo->role);
+                       if (bootverbose) {
+                               printf("(%s:%c:%d:%d): Received WDTR "
+                                      "%x filtered to %x\n",
+                                      ahd_name(ahd), devinfo->channel,
+                                      devinfo->target, devinfo->lun,
+                                      saved_width, bus_width);
+                       }
+
+                       if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, TRUE)) {
+                               /*
+                                * Don't send a WDTR back to the
+                                * target, since we asked first.
+                                * If the width went higher than our
+                                * request, reject it.
+                                */
+                               if (saved_width > bus_width) {
+                                       reject = TRUE;
+                                       printf("(%s:%c:%d:%d): requested %dBit "
+                                              "transfers.  Rejecting...\n",
+                                              ahd_name(ahd), devinfo->channel,
+                                              devinfo->target, devinfo->lun,
+                                              8 * (0x01 << bus_width));
+                                       bus_width = 0;
+                               }
+                       } else {
+                               /*
+                                * Send our own WDTR in reply
+                                */
+                               if (bootverbose
+                                && devinfo->role == ROLE_INITIATOR) {
+                                       printf("(%s:%c:%d:%d): Target "
+                                              "Initiated WDTR\n",
+                                              ahd_name(ahd), devinfo->channel,
+                                              devinfo->target, devinfo->lun);
+                               }
+                               ahd->msgout_index = 0;
+                               ahd->msgout_len = 0;
+                               ahd_construct_wdtr(ahd, devinfo, bus_width);
+                               ahd->msgout_index = 0;
+                               response = TRUE;
+                               sending_reply = TRUE;
+                       }
+                       ahd_set_width(ahd, devinfo, bus_width,
+                                     AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+                                     /*paused*/TRUE);
+                       /* After a wide message, we are async */
+                       ahd_set_syncrate(ahd, devinfo, /*period*/0,
+                                        /*offset*/0, /*ppr_options*/0,
+                                        AHD_TRANS_ACTIVE, /*paused*/TRUE);
+                       if (sending_reply == FALSE && reject == FALSE) {
+
+                               if (tinfo->goal.period) {
+                                       ahd->msgout_index = 0;
+                                       ahd->msgout_len = 0;
+                                       ahd_build_transfer_msg(ahd, devinfo);
+                                       ahd->msgout_index = 0;
+                                       response = TRUE;
+                               }
+                       }
+                       done = MSGLOOP_MSGCOMPLETE;
+                       break;
+               }
+               case MSG_EXT_PPR:
+               {
+                       u_int   period;
+                       u_int   offset;
+                       u_int   bus_width;
+                       u_int   ppr_options;
+                       u_int   saved_width;
+                       u_int   saved_offset;
+                       u_int   saved_ppr_options;
+
+                       if (ahd->msgin_buf[1] != MSG_EXT_PPR_LEN) {
+                               reject = TRUE;
+                               break;
+                       }
+
+                       /*
+                        * Wait until we have all args before validating
+                        * and acting on this message.
+                        *
+                        * Add one to MSG_EXT_PPR_LEN to account for
+                        * the extended message preamble.
+                        */
+                       if (ahd->msgin_index < (MSG_EXT_PPR_LEN + 1))
+                               break;
+
+                       period = ahd->msgin_buf[3];
+                       offset = ahd->msgin_buf[5];
+                       bus_width = ahd->msgin_buf[6];
+                       saved_width = bus_width;
+                       ppr_options = ahd->msgin_buf[7];
+                       /*
+                        * According to the spec, a DT only
+                        * period factor with no DT option
+                        * set implies async.
+                        */
+                       if ((ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+                        && period <= 9)
+                               offset = 0;
+                       saved_ppr_options = ppr_options;
+                       saved_offset = offset;
+
+                       /*
+                        * Transfer options are only available if we
+                        * are negotiating wide.
+                        */
+                       if (bus_width == 0)
+                               ppr_options &= MSG_EXT_PPR_QAS_REQ;
+
+                       ahd_validate_width(ahd, tinfo, &bus_width,
+                                          devinfo->role);
+                       ahd_devlimited_syncrate(ahd, tinfo, &period,
+                                               &ppr_options, devinfo->role);
+                       ahd_validate_offset(ahd, tinfo, period, &offset,
+                                           bus_width, devinfo->role);
+
+                       if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, TRUE)) {
+                               /*
+                                * If we are unable to do any of the
+                                * requested options (we went too low),
+                                * then we'll have to reject the message.
+                                */
+                               if (saved_width > bus_width
+                                || saved_offset != offset
+                                || saved_ppr_options != ppr_options) {
+                                       reject = TRUE;
+                                       period = 0;
+                                       offset = 0;
+                                       bus_width = 0;
+                                       ppr_options = 0;
+                               }
+                       } else {
+                               if (devinfo->role != ROLE_TARGET)
+                                       printf("(%s:%c:%d:%d): Target "
+                                              "Initiated PPR\n",
+                                              ahd_name(ahd), devinfo->channel,
+                                              devinfo->target, devinfo->lun);
+                               else
+                                       printf("(%s:%c:%d:%d): Initiator "
+                                              "Initiated PPR\n",
+                                              ahd_name(ahd), devinfo->channel,
+                                              devinfo->target, devinfo->lun);
+                               ahd->msgout_index = 0;
+                               ahd->msgout_len = 0;
+                               ahd_construct_ppr(ahd, devinfo, period, offset,
+                                                 bus_width, ppr_options);
+                               ahd->msgout_index = 0;
+                               response = TRUE;
+                       }
+                       if (bootverbose) {
+                               printf("(%s:%c:%d:%d): Received PPR width %x, "
+                                      "period %x, offset %x,options %x\n"
+                                      "\tFiltered to width %x, period %x, "
+                                      "offset %x, options %x\n",
+                                      ahd_name(ahd), devinfo->channel,
+                                      devinfo->target, devinfo->lun,
+                                      saved_width, ahd->msgin_buf[3],
+                                      saved_offset, saved_ppr_options,
+                                      bus_width, period, offset, ppr_options);
+                       }
+                       ahd_set_width(ahd, devinfo, bus_width,
+                                     AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+                                     /*paused*/TRUE);
+                       saved_ppr_options = tinfo->curr.ppr_options;
+                       if ((saved_ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
+#if AHD_DEBUG
+                               if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+                                       printf("PPR with IU_REQ outstanding\n");
+#endif
+                               ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE;
+                       }
+                       if ((saved_ppr_options & MSG_EXT_PPR_IU_REQ)
+                        != (ppr_options & MSG_EXT_PPR_IU_REQ)) {
+#if AHD_DEBUG
+                               if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
+                                       printf("Expecting IU Change busfree\n");
+#endif
+                               ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE
+                                              |  MSG_FLAG_IU_REQ_CHANGED;
+                       }
+
+                       ahd_set_syncrate(ahd, devinfo, period,
+                                        offset, ppr_options,
+                                        AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+                                        /*paused*/TRUE);
+
+                       done = MSGLOOP_MSGCOMPLETE;
+                       break;
+               }
+               default:
+                       /* Unknown extended message.  Reject it. */
+                       reject = TRUE;
+                       break;
+               }
+               break;
+       }
+#ifdef AHD_TARGET_MODE
+       case MSG_BUS_DEV_RESET:
+               ahd_handle_devreset(ahd, devinfo,
+                                   CAM_BDR_SENT,
+                                   "Bus Device Reset Received",
+                                   /*verbose_level*/0);
+               ahd_restart(ahd);
+               done = MSGLOOP_TERMINATED;
+               break;
+       case MSG_ABORT_TAG:
+       case MSG_ABORT:
+       case MSG_CLEAR_QUEUE:
+       {
+               int tag;
+
+               /* Target mode messages */
+               if (devinfo->role != ROLE_TARGET) {
+                       reject = TRUE;
+                       break;
+               }
+               tag = SCB_LIST_NULL;
+               if (ahd->msgin_buf[0] == MSG_ABORT_TAG)
+                       tag = ahd_inb(ahd, INITIATOR_TAG);
+               ahd_abort_scbs(ahd, devinfo->target, devinfo->channel,
+                              devinfo->lun, tag, ROLE_TARGET,
+                              CAM_REQ_ABORTED);
+
+               tstate = ahd->enabled_targets[devinfo->our_scsiid];
+               if (tstate != NULL) {
+                       struct ahd_tmode_lstate* lstate;
+
+                       lstate = tstate->enabled_luns[devinfo->lun];
+                       if (lstate != NULL) {
+                               ahd_queue_lstate_event(ahd, lstate,
+                                                      devinfo->our_scsiid,
+                                                      ahd->msgin_buf[0],
+                                                      /*arg*/tag);
+                               ahd_send_lstate_events(ahd, lstate);
+                       }
+               }
+               ahd_restart(ahd);
+               done = MSGLOOP_TERMINATED;
+               break;
+       }
+#endif
+       case MSG_QAS_REQUEST:
+               printf("%s: QAS request.  SCSISIGI == 0x%x\n",
+                      ahd_name(ahd), ahd_inb(ahd, SCSISIGI));
+               /* FALLTHROUGH */
+       case MSG_TERM_IO_PROC:
+       default:
+               reject = TRUE;
+               break;
+       }
+
+       if (reject) {
+               /*
+                * Setup to reject the message.
+                */
+               ahd->msgout_index = 0;
+               ahd->msgout_len = 1;
+               ahd->msgout_buf[0] = MSG_MESSAGE_REJECT;
+               done = MSGLOOP_MSGCOMPLETE;
+               response = TRUE;
+       }
+
+       if (done != MSGLOOP_IN_PROG && !response)
+               /* Clear the outgoing message buffer */
+               ahd->msgout_len = 0;
+
+       return (done);
+}
+
+/*
+ * Process a message reject message.
+ */
+static int
+ahd_handle_msg_reject(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+       /*
+        * What we care about here is if we had an
+        * outstanding SDTR or WDTR message for this
+        * target.  If we did, this is a signal that
+        * the target is refusing negotiation.
+        */
+       struct scb *scb;
+       struct ahd_initiator_tinfo *tinfo;
+       struct ahd_tmode_tstate *tstate;
+       u_int scb_index;
+       u_int last_msg;
+       int   response = 0;
+
+       scb_index = ahd_get_scbptr(ahd);
+       scb = ahd_lookup_scb(ahd, scb_index);
+       tinfo = ahd_fetch_transinfo(ahd, devinfo->channel,
+                                   devinfo->our_scsiid,
+                                   devinfo->target, &tstate);
+       /* Might be necessary */
+       last_msg = ahd_inb(ahd, LAST_MSG);
+
+       if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, /*full*/FALSE)) {
+               /*
+                * Target does not support the PPR message.
+                * Attempt to negotiate SPI-2 style.
+                */
+               if (bootverbose) {
+                       printf("(%s:%c:%d:%d): PPR Rejected. "
+                              "Trying WDTR/SDTR\n",
+                              ahd_name(ahd), devinfo->channel,
+                              devinfo->target, devinfo->lun);
+               }
+               tinfo->goal.ppr_options = 0;
+               tinfo->curr.transport_version = 2;
+               tinfo->goal.transport_version = 2;
+               ahd->msgout_index = 0;
+               ahd->msgout_len = 0;
+               ahd_build_transfer_msg(ahd, devinfo);
+               ahd->msgout_index = 0;
+               response = 1;
+       } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, /*full*/FALSE)) {
+
+               /* note 8bit xfers */
+               printf("(%s:%c:%d:%d): refuses WIDE negotiation.  Using "
+                      "8bit transfers\n", ahd_name(ahd),
+                      devinfo->channel, devinfo->target, devinfo->lun);
+               ahd_set_width(ahd, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+                             AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+                             /*paused*/TRUE);
+               /*
+                * No need to clear the sync rate.  If the target
+                * did not accept the command, our syncrate is
+                * unaffected.  If the target started the negotiation,
+                * but rejected our response, we already cleared the
+                * sync rate before sending our WDTR.
+                */
+               if (tinfo->goal.period) {
+
+                       /* Start the sync negotiation */
+                       ahd->msgout_index = 0;
+                       ahd->msgout_len = 0;
+                       ahd_build_transfer_msg(ahd, devinfo);
+                       ahd->msgout_index = 0;
+                       response = 1;
+               }
+       } else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, /*full*/FALSE)) {
+               /* note asynch xfers and clear flag */
+               ahd_set_syncrate(ahd, devinfo, /*period*/0,
+                                /*offset*/0, /*ppr_options*/0,
+                                AHD_TRANS_ACTIVE|AHD_TRANS_GOAL,
+                                /*paused*/TRUE);
+               printf("(%s:%c:%d:%d): refuses synchronous negotiation. "
+                      "Using asynchronous transfers\n",
+                      ahd_name(ahd), devinfo->channel,
+                      devinfo->target, devinfo->lun);
+       } else if ((scb->hscb->control & MSG_SIMPLE_TASK) != 0) {
+               int tag_type;
+               int mask;
+
+               tag_type = (scb->hscb->control & MSG_SIMPLE_TASK);
+
+               if (tag_type == MSG_SIMPLE_TASK) {
+                       printf("(%s:%c:%d:%d): refuses tagged commands.  "
+                              "Performing non-tagged I/O\n", ahd_name(ahd),
+                              devinfo->channel, devinfo->target, devinfo->lun);
+                       ahd_set_tags(ahd, devinfo, AHD_QUEUE_NONE);
+                       mask = ~0x23;
+               } else {
+                       printf("(%s:%c:%d:%d): refuses %s tagged commands.  "
+                              "Performing simple queue tagged I/O only\n",
+                              ahd_name(ahd), devinfo->channel, devinfo->target,
+                              devinfo->lun, tag_type == MSG_ORDERED_TASK
+                              ? "ordered" : "head of queue");
+                       ahd_set_tags(ahd, devinfo, AHD_QUEUE_BASIC);
+                       mask = ~0x03;
+               }
+
+               /*
+                * Resend the identify for this CCB as the target
+                * may believe that the selection is invalid otherwise.
+                */
+               ahd_outb(ahd, SCB_CONTROL,
+                        ahd_inb_scbram(ahd, SCB_CONTROL) & mask);
+               scb->hscb->control &= mask;
+               ahd_set_transaction_tag(scb, /*enabled*/FALSE,
+                                       /*type*/MSG_SIMPLE_TASK);
+               ahd_outb(ahd, MSG_OUT, MSG_IDENTIFYFLAG);
+               ahd_assert_atn(ahd);
+               ahd_busy_tcl(ahd, BUILD_TCL(scb->hscb->scsiid, devinfo->lun),
+                            SCB_GET_TAG(scb));
+
+               /*
+                * Requeue all tagged commands for this target
+                * currently in our posession so they can be
+                * converted to untagged commands.
+                */
+               ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb),
+                                  SCB_GET_CHANNEL(ahd, scb),
+                                  SCB_GET_LUN(scb), /*tag*/SCB_LIST_NULL,
+                                  ROLE_INITIATOR, CAM_REQUEUE_REQ,
+                                  SEARCH_COMPLETE);
+       } else {
+               /*
+                * Otherwise, we ignore it.
+                */
+               printf("%s:%c:%d: Message reject for %x -- ignored\n",
+                      ahd_name(ahd), devinfo->channel, devinfo->target,
+                      last_msg);
+       }
+       return (response);
+}
+
+/*
+ * Process an ingnore wide residue message.
+ */
+static void
+ahd_handle_ign_wide_residue(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+       u_int scb_index;
+       struct scb *scb;
+
+       scb_index = ahd_get_scbptr(ahd);
+       scb = ahd_lookup_scb(ahd, scb_index);
+       /*
+        * XXX Actually check data direction in the sequencer?
+        * Perhaps add datadir to some spare bits in the hscb?
+        */
+       if ((ahd_inb(ahd, SEQ_FLAGS) & DPHASE) == 0
+        || ahd_get_transfer_dir(scb) != CAM_DIR_IN) {
+               /*
+                * Ignore the message if we haven't
+                * seen an appropriate data phase yet.
+                */
+       } else {
+               /*
+                * If the residual occurred on the last
+                * transfer and the transfer request was
+                * expected to end on an odd count, do
+                * nothing.  Otherwise, subtract a byte
+                * and update the residual count accordingly.
+                */
+               uint32_t sgptr;
+
+               sgptr = ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR);
+               if ((sgptr & SG_LIST_NULL) != 0
+                && ahd_inb(ahd, DATA_COUNT_ODD) == 1) {
+                       /*
+                        * If the residual occurred on the last
+                        * transfer and the transfer request was
+                        * expected to end on an odd count, do
+                        * nothing.
+                        */
+               } else {
+                       uint32_t data_cnt;
+                       uint64_t data_addr;
+                       uint32_t sglen;
+
+                       /* Pull in the rest of the sgptr */
+                       sgptr |=
+                           (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 3) << 24)
+                         | (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 2) << 16)
+                         | (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 1) << 8);
+                       sgptr &= SG_PTR_MASK;
+                       data_cnt =
+                           (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT+3) << 24)
+                         | (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT+2) << 16)
+                         | (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT+1) << 8)
+                         | (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT));
+
+                       data_addr = (((uint64_t)ahd_inb(ahd, SHADDR + 7)) << 56)
+                                 | (((uint64_t)ahd_inb(ahd, SHADDR + 6)) << 48)
+                                 | (((uint64_t)ahd_inb(ahd, SHADDR + 5)) << 40)
+                                 | (((uint64_t)ahd_inb(ahd, SHADDR + 4)) << 32)
+                                 | (ahd_inb(ahd, SHADDR + 3) << 24)
+                                 | (ahd_inb(ahd, SHADDR + 2) << 16)
+                                 | (ahd_inb(ahd, SHADDR + 1) << 8)
+                                 | (ahd_inb(ahd, SHADDR));
+
+                       data_cnt += 1;
+                       data_addr -= 1;
+
+                       if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+                               struct ahd_dma64_seg *sg;
+
+                               sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+                               /*
+                                * The residual sg ptr points to the next S/G
+                                * to load so we must go back one.
+                                */
+                               sg--;
+                               sglen = ahd_le32toh(sg->len) & AHD_SG_LEN_MASK;
+                               if (sg != scb->sg_list
+                                && sglen < (data_cnt & AHD_SG_LEN_MASK)) {
+
+                                       sg--;
+                                       sglen = ahd_le32toh(sg->len);
+                                       /*
+                                        * Preserve High Address and SG_LIST
+                                        * bits while setting the count to 1.
+                                        */
+                                       data_cnt = 1|(sglen&(~AHD_SG_LEN_MASK));
+                                       data_addr = ahd_le64toh(sg->addr)
+                                                 + (sglen & AHD_SG_LEN_MASK)
+                                                 - 1;
+
+                                       /*
+                                        * Increment sg so it points to the
+                                        * "next" sg.
+                                        */
+                                       sg++;
+                                       sgptr = ahd_sg_virt_to_bus(ahd, scb,
+                                                                  sg);
+                               }
+                       } else {
+                               struct ahd_dma_seg *sg;
+
+                               sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+                               /*
+                                * The residual sg ptr points to the next S/G
+                                * to load so we must go back one.
+                                */
+                               sg--;
+                               sglen = ahd_le32toh(sg->len) & AHD_SG_LEN_MASK;
+                               if (sg != scb->sg_list
+                                && sglen < (data_cnt & AHD_SG_LEN_MASK)) {
+
+                                       sg--;
+                                       sglen = ahd_le32toh(sg->len);
+                                       /*
+                                        * Preserve High Address and SG_LIST
+                                        * bits while setting the count to 1.
+                                        */
+                                       data_cnt = 1|(sglen&(~AHD_SG_LEN_MASK));
+                                       data_addr = ahd_le32toh(sg->addr)
+                                                 + (sglen & AHD_SG_LEN_MASK)
+                                                 - 1;
+
+                                       /*
+                                        * Increment sg so it points to the
+                                        * "next" sg.
+                                        */
+                                       sg++;
+                                       sgptr = ahd_sg_virt_to_bus(ahd, scb,
+                                                                 sg);
+                               }
+                       }
+                       ahd_outb(ahd, SCB_RESIDUAL_SGPTR + 3, sgptr >> 24);
+                       ahd_outb(ahd, SCB_RESIDUAL_SGPTR + 2, sgptr >> 16);
+                       ahd_outb(ahd, SCB_RESIDUAL_SGPTR + 1, sgptr >> 8);
+                       ahd_outb(ahd, SCB_RESIDUAL_SGPTR, sgptr);
+
+                       ahd_outb(ahd, SCB_RESIDUAL_DATACNT + 3, data_cnt >> 24);
+                       ahd_outb(ahd, SCB_RESIDUAL_DATACNT + 2, data_cnt >> 16);
+                       ahd_outb(ahd, SCB_RESIDUAL_DATACNT + 1, data_cnt >> 8);
+                       ahd_outb(ahd, SCB_RESIDUAL_DATACNT, data_cnt);
+
+                       /*
+                        * The FIFO's pointers will be updated if/when the
+                        * sequencer re-enters a data phase.
+                        */
+               }
+       }
+}
+
+
+/*
+ * Reinitialize the data pointers for the active transfer
+ * based on its current residual.
+ */
+static void
+ahd_reinitialize_dataptrs(struct ahd_softc *ahd)
+{
+       struct           scb *scb;
+       ahd_mode_state   saved_modes;
+       u_int            scb_index;
+       u_int            wait;
+       uint32_t         sgptr;
+       uint32_t         resid;
+       uint64_t         dataptr;
+
+       AHD_ASSERT_MODES(ahd, AHD_MODE_DFF0_MSK|AHD_MODE_DFF1_MSK,
+                        AHD_MODE_DFF0_MSK|AHD_MODE_DFF1_MSK);
+                        
+       scb_index = ahd_get_scbptr(ahd);
+       scb = ahd_lookup_scb(ahd, scb_index);
+
+       /*
+        * Release and reacquire the FIFO so we
+        * have a clean slate.
+        */
+       ahd_outb(ahd, DFFSXFRCTL, CLRCHN);
+       wait = 1000;
+       do {
+               ahd_delay(100);
+       } while (--wait && !(ahd_inb(ahd, MDFFSTAT) & FIFOFREE));
+       if (wait == 0) {
+               ahd_print_path(ahd, scb);
+               printf("ahd_reinitialize_dataptrs: Forcing FIFO free.\n");
+               ahd_outb(ahd, DFFSXFRCTL, RSTCHN|CLRSHCNT);
+       }
+       saved_modes = ahd_save_modes(ahd);
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       ahd_outb(ahd, DFFSTAT,
+                ahd_inb(ahd, DFFSTAT) | (saved_modes == 0x11 ? CURRFIFO : 0));
+
+       /*
+        * Determine initial values for data_addr and data_cnt
+        * for resuming the data phase.
+        */
+       sgptr = (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 3) << 24)
+             | (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 2) << 16)
+             | (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR + 1) << 8)
+             | ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR);
+       sgptr &= SG_PTR_MASK;
+
+       resid = (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT + 2) << 16)
+             | (ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT + 1) << 8)
+             | ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT);
+
+       if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+               struct ahd_dma64_seg *sg;
+
+               sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+               /* The residual sg_ptr always points to the next sg */
+               sg--;
+
+               dataptr = ahd_le64toh(sg->addr)
+                       + (ahd_le32toh(sg->len) & AHD_SG_LEN_MASK)
+                       - resid;
+               ahd_outb(ahd, HADDR + 7, dataptr >> 56);
+               ahd_outb(ahd, HADDR + 6, dataptr >> 48);
+               ahd_outb(ahd, HADDR + 5, dataptr >> 40);
+               ahd_outb(ahd, HADDR + 4, dataptr >> 32);
+       } else {
+               struct   ahd_dma_seg *sg;
+
+               sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
+
+               /* The residual sg_ptr always points to the next sg */
+               sg--;
+
+               dataptr = ahd_le32toh(sg->addr)
+                       + (ahd_le32toh(sg->len) & AHD_SG_LEN_MASK)
+                       - resid;
+               ahd_outb(ahd, HADDR + 4,
+                        (ahd_le32toh(sg->len) & ~AHD_SG_LEN_MASK) >> 24);
+       }
+       ahd_outb(ahd, HADDR + 3, dataptr >> 24);
+       ahd_outb(ahd, HADDR + 2, dataptr >> 16);
+       ahd_outb(ahd, HADDR + 1, dataptr >> 8);
+       ahd_outb(ahd, HADDR, dataptr);
+       ahd_outb(ahd, HCNT + 2, resid >> 16);
+       ahd_outb(ahd, HCNT + 1, resid >> 8);
+       ahd_outb(ahd, HCNT, resid);
+}
+
+/*
+ * Handle the effects of issuing a bus device reset message.
+ */
+static void
+ahd_handle_devreset(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+                   cam_status status, char *message, int verbose_level)
+{
+#ifdef AHD_TARGET_MODE
+       struct ahd_tmode_tstate* tstate;
+       u_int lun;
+#endif
+       int found;
+
+       found = ahd_abort_scbs(ahd, devinfo->target, devinfo->channel,
+                              CAM_LUN_WILDCARD, SCB_LIST_NULL, devinfo->role,
+                              status);
+
+#ifdef AHD_TARGET_MODE
+       /*
+        * Send an immediate notify ccb to all target mord peripheral
+        * drivers affected by this action.
+        */
+       tstate = ahd->enabled_targets[devinfo->our_scsiid];
+       if (tstate != NULL) {
+               for (lun = 0; lun < AHD_NUM_LUNS; lun++) {
+                       struct ahd_tmode_lstate* lstate;
+
+                       lstate = tstate->enabled_luns[lun];
+                       if (lstate == NULL)
+                               continue;
+
+                       ahd_queue_lstate_event(ahd, lstate, devinfo->our_scsiid,
+                                              MSG_BUS_DEV_RESET, /*arg*/0);
+                       ahd_send_lstate_events(ahd, lstate);
+               }
+       }
+#endif
+
+       /*
+        * Go back to async/narrow transfers and renegotiate.
+        */
+       ahd_set_width(ahd, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+                     AHD_TRANS_CUR, /*paused*/TRUE);
+       ahd_set_syncrate(ahd, devinfo, /*period*/0, /*offset*/0,
+                        /*ppr_options*/0, AHD_TRANS_CUR, /*paused*/TRUE);
+       
+       ahd_send_async(ahd, devinfo->channel, devinfo->target,
+                      CAM_LUN_WILDCARD, AC_SENT_BDR, NULL);
+
+       if (message != NULL
+        && (verbose_level <= bootverbose))
+               printf("%s: %s on %c:%d. %d SCBs aborted\n", ahd_name(ahd),
+                      message, devinfo->channel, devinfo->target, found);
+}
+
+#ifdef AHD_TARGET_MODE
+static void
+ahd_setup_target_msgin(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+                      struct scb *scb)
+{
+
+       /*              
+        * To facilitate adding multiple messages together,
+        * each routine should increment the index and len
+        * variables instead of setting them explicitly.
+        */             
+       ahd->msgout_index = 0;
+       ahd->msgout_len = 0;
+
+       if (scb != NULL && (scb->flags & SCB_AUTO_NEGOTIATE) != 0)
+               ahd_build_transfer_msg(ahd, devinfo);
+       else
+               panic("ahd_intr: AWAITING target message with no message");
+
+       ahd->msgout_index = 0;
+       ahd->msg_type = MSG_TYPE_TARGET_MSGIN;
+}
+#endif
+/**************************** Initialization **********************************/
+static bus_size_t
+ahd_sglist_size(struct ahd_softc *ahd)
+{
+       bus_size_t list_size;
+
+       list_size = sizeof(struct ahd_dma_seg) * AHD_NSEG;
+       if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+               list_size = sizeof(struct ahd_dma64_seg) * AHD_NSEG;
+       return (list_size);
+}
+
+/*
+ * Calculate the optimum S/G List allocation size.  S/G elements used
+ * for a given transaction must be physically contiguous.  Assume the
+ * OS will allocate full pages to us, so it doesn't make sense to request
+ * less than a page.
+ */
+static bus_size_t
+ahd_sglist_allocsize(struct ahd_softc *ahd)
+{
+       bus_size_t sg_list_increment;
+       bus_size_t sg_list_size;
+       bus_size_t max_list_size;
+       bus_size_t best_list_size;
+
+       /* Start out with the minimum required for AHD_NSEG. */
+       sg_list_increment = ahd_sglist_size(ahd);
+       sg_list_size = sg_list_increment;
+
+       /* Get us as close as possible to a page in size. */
+       while ((sg_list_size + sg_list_increment) <= PAGE_SIZE)
+               sg_list_size += sg_list_increment;
+
+       /*
+        * Try to reduce the amount of wastage by allocating
+        * multiple pages.
+        */
+       best_list_size = sg_list_size;
+       max_list_size = roundup(sg_list_increment, PAGE_SIZE);
+       if (max_list_size < 4 * PAGE_SIZE)
+               max_list_size = 4 * PAGE_SIZE;
+       if (max_list_size > (AHD_SCB_MAX_ALLOC * sg_list_increment))
+               max_list_size = (AHD_SCB_MAX_ALLOC * sg_list_increment);
+       while ((sg_list_size + sg_list_increment) <= max_list_size
+          &&  (sg_list_size % PAGE_SIZE) != 0) {
+               bus_size_t new_mod;
+               bus_size_t best_mod;
+
+               sg_list_size += sg_list_increment;
+               new_mod = sg_list_size % PAGE_SIZE;
+               best_mod = best_list_size % PAGE_SIZE;
+               if (new_mod > best_mod || new_mod == 0) {
+                       best_list_size = sg_list_size;
+               }
+       }
+       return (best_list_size);
+}
+
+/*
+ * Allocate a controller structure for a new device
+ * and perform initial initializion.
+ */
+struct ahd_softc *
+ahd_alloc(void *platform_arg, char *name)
+{
+       struct  ahd_softc *ahd;
+
+#ifndef        __FreeBSD__
+       ahd = malloc(sizeof(*ahd), M_DEVBUF, M_NOWAIT);
+       if (!ahd) {
+               printf("aic7xxx: cannot malloc softc!\n");
+               free(name, M_DEVBUF);
+               return NULL;
+       }
+#else
+       ahd = device_get_softc((device_t)platform_arg);
+#endif
+       memset(ahd, 0, sizeof(*ahd));
+       ahd->seep_config = malloc(sizeof(*ahd->seep_config),
+                                 M_DEVBUF, M_NOWAIT);
+       if (ahd->seep_config == NULL) {
+#ifndef        __FreeBSD__
+               free(ahd, M_DEVBUF);
+#endif
+               free(name, M_DEVBUF);
+               return (NULL);
+       }
+       LIST_INIT(&ahd->pending_scbs);
+       /* We don't know our unit number until the OSM sets it */
+       ahd->name = name;
+       ahd->unit = -1;
+       ahd->description = NULL;
+       ahd->bus_description = NULL;
+       ahd->channel = 'A';
+       ahd->chip = AHD_NONE;
+       ahd->features = AHD_FENONE;
+       ahd->bugs = AHD_BUGNONE;
+       ahd->flags = AHD_SPCHK_ENB_A|AHD_RESET_BUS_A|AHD_TERM_ENB_A
+                  | AHD_EXTENDED_TRANS_A|AHD_STPWLEVEL_A;
+       ahd_timer_init(&ahd->reset_timer);
+
+       if (ahd_platform_alloc(ahd, platform_arg) != 0) {
+               ahd_free(ahd);
+               ahd = NULL;
+       }
+       return (ahd);
+}
+
+int
+ahd_softc_init(struct ahd_softc *ahd)
+{
+
+       ahd->unpause = 0;
+       ahd->pause = PAUSE; 
+       return (0);
+}
+
+void
+ahd_softc_insert(struct ahd_softc *ahd)
+{
+       struct ahd_softc *list_ahd;
+
+#if AHD_PCI_CONFIG > 0
+       /*
+        * Second Function PCI devices need to inherit some
+        * settings from function 0.
+        */
+       if ((ahd->features & AHD_MULTI_FUNC) != 0) {
+               TAILQ_FOREACH(list_ahd, &ahd_tailq, links) {
+                       ahd_dev_softc_t list_pci;
+                       ahd_dev_softc_t pci;
+
+                       list_pci = list_ahd->dev_softc;
+                       pci = ahd->dev_softc;
+                       if (ahd_get_pci_slot(list_pci) == ahd_get_pci_slot(pci)
+                        && ahd_get_pci_bus(list_pci) == ahd_get_pci_bus(pci)) {
+                               struct ahd_softc *master;
+                               struct ahd_softc *slave;
+
+                               if (ahd_get_pci_function(list_pci) == 0) {
+                                       master = list_ahd;
+                                       slave = ahd;
+                               } else {
+                                       master = ahd;
+                                       slave = list_ahd;
+                               }
+                               slave->flags &= ~AHD_BIOS_ENABLED; 
+                               slave->flags |=
+                                   master->flags & AHD_BIOS_ENABLED;
+                               slave->flags &= ~AHD_PRIMARY_CHANNEL; 
+                               slave->flags |=
+                                   master->flags & AHD_PRIMARY_CHANNEL;
+                               break;
+                       }
+               }
+       }
+#endif
+
+       /*
+        * Insertion sort into our list of softcs.
+        */
+       list_ahd = TAILQ_FIRST(&ahd_tailq);
+       while (list_ahd != NULL
+           && ahd_softc_comp(list_ahd, ahd) <= 0)
+               list_ahd = TAILQ_NEXT(list_ahd, links);
+       if (list_ahd != NULL)
+               TAILQ_INSERT_BEFORE(list_ahd, ahd, links);
+       else
+               TAILQ_INSERT_TAIL(&ahd_tailq, ahd, links);
+       ahd->init_level++;
+}
+
+/*
+ * Verify that the passed in softc pointer is for a
+ * controller that is still configured.
+ */
+struct ahd_softc *
+ahd_find_softc(struct ahd_softc *ahd)
+{
+       struct ahd_softc *list_ahd;
+
+       TAILQ_FOREACH(list_ahd, &ahd_tailq, links) {
+               if (list_ahd == ahd)
+                       return (ahd);
+       }
+       return (NULL);
+}
+
+void
+ahd_set_unit(struct ahd_softc *ahd, int unit)
+{
+       ahd->unit = unit;
+}
+
+void
+ahd_set_name(struct ahd_softc *ahd, char *name)
+{
+       if (ahd->name != NULL)
+               free(ahd->name, M_DEVBUF);
+       ahd->name = name;
+}
+
+void
+ahd_free(struct ahd_softc *ahd)
+{
+       int i;
+
+       ahd_fini_scbdata(ahd);
+       switch (ahd->init_level) {
+       default:
+       case 5:
+               ahd_shutdown(ahd);
+               TAILQ_REMOVE(&ahd_tailq, ahd, links);
+               /* FALLTHROUGH */
+       case 4:
+               ahd_dmamap_unload(ahd, ahd->shared_data_dmat,
+                                 ahd->shared_data_dmamap);
+               /* FALLTHROUGH */
+       case 3:
+               ahd_dmamem_free(ahd, ahd->shared_data_dmat, ahd->qoutfifo,
+                               ahd->shared_data_dmamap);
+               ahd_dmamap_destroy(ahd, ahd->shared_data_dmat,
+                                  ahd->shared_data_dmamap);
+               /* FALLTHROUGH */
+       case 2:
+               ahd_dma_tag_destroy(ahd, ahd->shared_data_dmat);
+       case 1:
+#ifndef __linux__
+               ahd_dma_tag_destroy(ahd, ahd->buffer_dmat);
+#endif
+               break;
+       case 0:
+               break;
+       }
+
+#ifndef __linux__
+       ahd_dma_tag_destroy(ahd, ahd->parent_dmat);
+#endif
+       ahd_platform_free(ahd);
+       for (i = 0; i < AHD_NUM_TARGETS; i++) {
+               struct ahd_tmode_tstate *tstate;
+
+               tstate = ahd->enabled_targets[i];
+               if (tstate != NULL) {
+#if AHD_TARGET_MODE
+                       int j;
+
+                       for (j = 0; j < AHD_NUM_LUNS; j++) {
+                               struct ahd_tmode_lstate *lstate;
+
+                               lstate = tstate->enabled_luns[j];
+                               if (lstate != NULL) {
+                                       xpt_free_path(lstate->path);
+                                       free(lstate, M_DEVBUF);
+                               }
+                       }
+#endif
+                       free(tstate, M_DEVBUF);
+               }
+       }
+#if AHD_TARGET_MODE
+       if (ahd->black_hole != NULL) {
+               xpt_free_path(ahd->black_hole->path);
+               free(ahd->black_hole, M_DEVBUF);
+       }
+#endif
+       if (ahd->name != NULL)
+               free(ahd->name, M_DEVBUF);
+       if (ahd->seep_config != NULL)
+               free(ahd->seep_config, M_DEVBUF);
+#ifndef __FreeBSD__
+       free(ahd, M_DEVBUF);
+#endif
+       return;
+}
+
+void
+ahd_shutdown(void *arg)
+{
+       struct  ahd_softc *ahd;
+
+       ahd = (struct ahd_softc *)arg;
+
+       /* This will reset most registers to 0, but not all */
+       ahd_reset(ahd);
+}
+
+/*
+ * Reset the controller and record some information about it
+ * that is only available just after a reset.
+ */
+int
+ahd_reset(struct ahd_softc *ahd)
+{
+       u_int    sxfrctl1;
+       int      wait;
+       uint32_t cmd;
+       
+       /*
+        * Preserve the value of the SXFRCTL1 register for all channels.
+        * It contains settings that affect termination and we don't want
+        * to disturb the integrity of the bus.
+        */
+       ahd_pause(ahd);
+       sxfrctl1 = ahd_inb(ahd, SXFRCTL1);
+
+       cmd = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/2);
+       if ((ahd->bugs & AHD_PCIX_CHIPRST_BUG) != 0) {
+               uint32_t mod_cmd;
+
+               /*
+                * A4 Razor #632
+                * During the assertion of CHIPRST, the chip
+                * does not disable its parity logic prior to
+                * the start of the reset.  This may cause a
+                * parity error to be detected and thus a
+                * spurious SERR or PERR assertion.  Disble
+                * PERR and SERR responses during the CHIPRST.
+                */
+               mod_cmd = cmd & ~(PCIM_CMD_PERRESPEN|PCIM_CMD_SERRESPEN);
+               ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+                                    mod_cmd, /*bytes*/2);
+       }
+       ahd_outb(ahd, HCNTRL, CHIPRST | ahd->pause);
+
+       /*
+        * Ensure that the reset has finished.  We delay 1000us
+        * prior to reading the register to make sure the chip
+        * has sufficiently completed its reset to handle register
+        * accesses.
+        */
+       wait = 1000;
+       do {
+               ahd_delay(1000);
+       } while (--wait && !(ahd_inb(ahd, HCNTRL) & CHIPRSTACK));
+
+       if (wait == 0) {
+               printf("%s: WARNING - Failed chip reset!  "
+                      "Trying to initialize anyway.\n", ahd_name(ahd));
+       }
+       ahd_outb(ahd, HCNTRL, ahd->pause);
+
+       if ((ahd->bugs & AHD_PCIX_CHIPRST_BUG) != 0) {
+               /*
+                * Clear any latched PCI error status and restore
+                * previous SERR and PERR response enables.
+                */
+               ahd_pci_write_config(ahd->dev_softc, PCIR_STATUS + 1,
+                                    0xFF, /*bytes*/1);
+               ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+                                    cmd, /*bytes*/2);
+       }
+       /* After a reset, we know the state of the mode register. */
+       ahd_known_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+       /* Determine chip configuration */
+       ahd->features &= ~AHD_WIDE;
+       if ((ahd_inb(ahd, SBLKCTL) & SELWIDE) != 0)
+               ahd->features |= AHD_WIDE;
+
+       /*
+        * Restore SXFRCTL1.
+        *
+        * We must always initialize STPWEN to 1 before we
+        * restore the saved values.  STPWEN is initialized
+        * to a tri-state condition which can only be cleared
+        * by turning it on.
+        */
+       ahd_outb(ahd, SXFRCTL1, sxfrctl1|STPWEN);
+       ahd_outb(ahd, SXFRCTL1, sxfrctl1);
+
+       /*
+        * If a recovery action has forced a chip reset,
+        * re-initialize the chip to our likeing.
+        */
+       if (ahd->init_level > 0)
+               ahd_chip_init(ahd);
+
+       return (0);
+}
+
+/*
+ * Determine the number of SCBs available on the controller
+ */
+int
+ahd_probe_scbs(struct ahd_softc *ahd) {
+       int i;
+
+       AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+                        ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+       for (i = 0; i < AHD_SCB_MAX; i++) {
+               int j;
+
+               ahd_set_scbptr(ahd, i);
+               ahd_outw(ahd, SCB_BASE, i);
+               for (j = 2; j < 64; j++)
+                       ahd_outb(ahd, SCB_BASE+j, 0);
+               /* Start out life as unallocated (needing an abort) */
+               ahd_outb(ahd, SCB_CONTROL, MK_MESSAGE);
+               if (ahd_inw_scbram(ahd, SCB_BASE) != i)
+                       break;
+               ahd_set_scbptr(ahd, 0);
+               if (ahd_inw_scbram(ahd, SCB_BASE) != 0)
+                       break;
+       }
+       return (i);
+}
+
+static void
+ahd_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 
+{
+       bus_addr_t *baddr;
+
+       baddr = (bus_addr_t *)arg;
+       *baddr = segs->ds_addr;
+}
+
+static void
+ahd_initialize_hscbs(struct ahd_softc *ahd)
+{
+       int i;
+
+       for (i = 0; i < ahd->scb_data.maxhscbs; i++) {
+               ahd_set_scbptr(ahd, i);
+
+               /* Clear the control byte. */
+               ahd_outb(ahd, SCB_CONTROL, 0);
+
+               /* Set the next pointer */
+               ahd_outw(ahd, SCB_NEXT, SCB_LIST_NULL);
+       }
+}
+
+static int
+ahd_init_scbdata(struct ahd_softc *ahd)
+{
+       struct scb_data *scb_data;
+
+       scb_data = &ahd->scb_data;
+       SLIST_INIT(&scb_data->free_scbs);
+       SLIST_INIT(&scb_data->hscb_maps);
+       SLIST_INIT(&scb_data->sg_maps);
+       SLIST_INIT(&scb_data->sense_maps);
+
+       /* Determine the number of hardware SCBs and initialize them */
+       scb_data->maxhscbs = ahd_probe_scbs(ahd);
+       if (scb_data->maxhscbs == 0) {
+               printf("%s: No SCB space found\n", ahd_name(ahd));
+               return (ENXIO);
+       }
+
+       ahd_initialize_hscbs(ahd);
+
+       /*
+        * Create our DMA tags.  These tags define the kinds of device
+        * accessible memory allocations and memory mappings we will
+        * need to perform during normal operation.
+        *
+        * Unless we need to further restrict the allocation, we rely
+        * on the restrictions of the parent dmat, hence the common
+        * use of MAXADDR and MAXSIZE.
+        */
+
+       /* DMA tag for our hardware scb structures */
+       if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+                              /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+                              /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+                              /*highaddr*/BUS_SPACE_MAXADDR,
+                              /*filter*/NULL, /*filterarg*/NULL,
+                              PAGE_SIZE, /*nsegments*/1,
+                              /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+                              /*flags*/0, &scb_data->hscb_dmat) != 0) {
+               goto error_exit;
+       }
+
+       scb_data->init_level++;
+
+       /* DMA tag for our S/G structures. */
+       if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+                              /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+                              /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+                              /*highaddr*/BUS_SPACE_MAXADDR,
+                              /*filter*/NULL, /*filterarg*/NULL,
+                              ahd_sglist_allocsize(ahd), /*nsegments*/1,
+                              /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+                              /*flags*/0, &scb_data->sg_dmat) != 0) {
+               goto error_exit;
+       }
+#if AHD_DEBUG
+       if ((ahd_debug & AHD_SHOW_MEMORY) != 0)
+               printf("%s: ahd_sglist_allocsize = 0x%x\n", ahd_name(ahd),
+                      ahd_sglist_allocsize(ahd));
+#endif
+
+       scb_data->init_level++;
+
+       /* DMA tag for our sense buffers.  We allocate in page sized chunks */
+       if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+                              /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+                              /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+                              /*highaddr*/BUS_SPACE_MAXADDR,
+                              /*filter*/NULL, /*filterarg*/NULL,
+                              PAGE_SIZE, /*nsegments*/1,
+                              /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+                              /*flags*/0, &scb_data->sense_dmat) != 0) {
+               goto error_exit;
+       }
+
+       scb_data->init_level++;
+
+       /* Perform initial CCB allocation */
+       ahd_alloc_scbs(ahd);
+
+       if (scb_data->numscbs == 0) {
+               printf("%s: ahd_init_scbdata - "
+                      "Unable to allocate initial scbs\n",
+                      ahd_name(ahd));
+               goto error_exit;
+       }
+
+       /*
+        * Reserve an SCB as the initial "next SCB" to be
+        * queued to the controller.
+        */
+       ahd->next_queued_scb = ahd_get_scb(ahd);
+
+       /*
+        * Note that we were successfull
+        */
+       return (0); 
+
+error_exit:
+
+       return (ENOMEM);
+}
+
+static void
+ahd_fini_scbdata(struct ahd_softc *ahd)
+{
+       struct scb_data *scb_data;
+
+       scb_data = &ahd->scb_data;
+       if (scb_data == NULL)
+               return;
+
+       switch (scb_data->init_level) {
+       default:
+       case 7:
+       {
+               struct map_node *sns_map;
+
+               while ((sns_map = SLIST_FIRST(&scb_data->sense_maps)) != NULL) {
+                       SLIST_REMOVE_HEAD(&scb_data->sense_maps, links);
+                       ahd_dmamap_unload(ahd, scb_data->sense_dmat,
+                                         sns_map->dmamap);
+                       ahd_dmamem_free(ahd, scb_data->sense_dmat,
+                                       sns_map->vaddr, sns_map->dmamap);
+                       free(sns_map, M_DEVBUF);
+               }
+               ahd_dma_tag_destroy(ahd, scb_data->sense_dmat);
+               /* FALLTHROUGH */
+       }
+       case 6:
+       {
+               struct map_node *sg_map;
+
+               while ((sg_map = SLIST_FIRST(&scb_data->sg_maps)) != NULL) {
+                       SLIST_REMOVE_HEAD(&scb_data->sg_maps, links);
+                       ahd_dmamap_unload(ahd, scb_data->sg_dmat,
+                                         sg_map->dmamap);
+                       ahd_dmamem_free(ahd, scb_data->sg_dmat,
+                                       sg_map->vaddr, sg_map->dmamap);
+                       free(sg_map, M_DEVBUF);
+               }
+               ahd_dma_tag_destroy(ahd, scb_data->sg_dmat);
+               /* FALLTHROUGH */
+       }
+       case 5:
+       {
+               struct map_node *hscb_map;
+
+               while ((hscb_map = SLIST_FIRST(&scb_data->hscb_maps)) != NULL) {
+                       SLIST_REMOVE_HEAD(&scb_data->hscb_maps, links);
+                       ahd_dmamap_unload(ahd, scb_data->hscb_dmat,
+                                         hscb_map->dmamap);
+                       ahd_dmamem_free(ahd, scb_data->hscb_dmat,
+                                       hscb_map->vaddr, hscb_map->dmamap);
+                       free(hscb_map, M_DEVBUF);
+               }
+               ahd_dma_tag_destroy(ahd, scb_data->hscb_dmat);
+               /* FALLTHROUGH */
+       }
+       case 4:
+       case 3:
+       case 2:
+       case 1:
+       case 0:
+               break;
+       }
+}
+
+/*
+ * DSP filter Bypass must be enabled until the first selection
+ * after a change in bus mode (Razor #491 and #493).
+ */
+static void
+ahd_setup_iocell_workaround(struct ahd_softc *ahd)
+{
+       ahd_mode_state saved_modes;
+
+       saved_modes = ahd_save_modes(ahd);
+       ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+       ahd_outb(ahd, DSPDATACTL, ahd_inb(ahd, DSPDATACTL)
+              | BYPASSENAB | RCVROFFSTDIS | XMITOFFSTDIS);
+       ahd_outb(ahd, SIMODE0, ahd_inb(ahd, SIMODE0) | (ENSELDO|ENSELDI));
+       ahd_restore_modes(ahd, saved_modes);
+}
+
+static void
+ahd_iocell_first_selection(struct ahd_softc *ahd)
+{
+       ahd_mode_state  saved_modes;
+       u_int           sblkctl;
+
+       saved_modes = ahd_save_modes(ahd);
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       sblkctl = ahd_inb(ahd, SBLKCTL);
+       ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+       if ((sblkctl & ENAB40) != 0) {
+               ahd_outb(ahd, DSPDATACTL,
+                        ahd_inb(ahd, DSPDATACTL) & ~BYPASSENAB);
+       }
+       ahd_outb(ahd, SIMODE0, ahd_inb(ahd, SIMODE0) & ~(ENSELDO|ENSELDI));
+       ahd_outb(ahd, CLRINT, CLRSCSIINT);
+       ahd_restore_modes(ahd, saved_modes);
+}
+
+void
+ahd_alloc_scbs(struct ahd_softc *ahd)
+{
+       struct scb_data *scb_data;
+       struct scb      *next_scb;
+       struct hardware_scb *hscb;
+       struct map_node *hscb_map;
+       struct map_node *sg_map;
+       struct map_node *sense_map;
+       uint8_t         *segs;
+       uint8_t         *sense_data;
+       bus_addr_t       hscb_busaddr;
+       bus_addr_t       sg_busaddr;
+       bus_addr_t       sense_busaddr;
+       int              newcount;
+       int              i;
+
+       scb_data = &ahd->scb_data;
+       if (scb_data->numscbs >= AHD_SCB_MAX_ALLOC)
+               /* Can't allocate any more */
+               return;
+
+       if (scb_data->scbs_left != 0) {
+               int offset;
+
+               offset = (PAGE_SIZE / sizeof(*hscb)) - scb_data->scbs_left;
+               hscb_map = SLIST_FIRST(&scb_data->hscb_maps);
+               hscb = &((struct hardware_scb *)hscb_map->vaddr)[offset];
+               hscb_busaddr = hscb_map->physaddr + (offset * sizeof(*hscb));
+       } else {
+               hscb_map = malloc(sizeof(*hscb_map), M_DEVBUF, M_NOWAIT);
+
+               if (hscb_map == NULL)
+                       return;
+
+               /* Allocate the next batch of hardware SCBs */
+               if (ahd_dmamem_alloc(ahd, scb_data->hscb_dmat,
+                                    (void **)&hscb_map->vaddr,
+                                    BUS_DMA_NOWAIT, &hscb_map->dmamap) != 0) {
+                       free(hscb_map, M_DEVBUF);
+                       return;
+               }
+
+               SLIST_INSERT_HEAD(&scb_data->hscb_maps, hscb_map, links);
+
+               ahd_dmamap_load(ahd, scb_data->hscb_dmat, hscb_map->dmamap,
+                               hscb_map->vaddr, PAGE_SIZE, ahd_dmamap_cb,
+                               &hscb_map->physaddr, /*flags*/0);
+
+               hscb = (struct hardware_scb *)hscb_map->vaddr;
+               hscb_busaddr = hscb_map->physaddr;
+               scb_data->scbs_left = PAGE_SIZE / sizeof(*hscb);
+       }
+
+       if (scb_data->sgs_left != 0) {
+               int offset;
+
+               offset = ahd_sglist_allocsize(ahd)
+                      - (scb_data->sgs_left * ahd_sglist_size(ahd));
+               sg_map = SLIST_FIRST(&scb_data->sg_maps);
+               segs = sg_map->vaddr + offset;
+               sg_busaddr = sg_map->physaddr + offset;
+       } else {
+               sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT);
+
+               if (sg_map == NULL)
+                       return;
+
+               /* Allocate the next batch of S/G lists */
+               if (ahd_dmamem_alloc(ahd, scb_data->sg_dmat,
+                                    (void **)&sg_map->vaddr,
+                                    BUS_DMA_NOWAIT, &sg_map->dmamap) != 0) {
+                       free(sg_map, M_DEVBUF);
+                       return;
+               }
+
+               SLIST_INSERT_HEAD(&scb_data->sg_maps, sg_map, links);
+
+               ahd_dmamap_load(ahd, scb_data->sg_dmat, sg_map->dmamap,
+                               sg_map->vaddr, ahd_sglist_allocsize(ahd),
+                               ahd_dmamap_cb, &sg_map->physaddr, /*flags*/0);
+
+               segs = sg_map->vaddr;
+               sg_busaddr = sg_map->physaddr;
+               scb_data->sgs_left =
+                   ahd_sglist_allocsize(ahd) / ahd_sglist_size(ahd);
+#ifdef AHD_DEBUG
+               if (ahd_debug & AHD_SHOW_MEMORY)
+                       printf("Mapped SG data\n");
+#endif
+       }
+
+       if (scb_data->sense_left != 0) {
+               int offset;
+
+               offset = PAGE_SIZE - (AHD_SENSE_BUFSIZE * scb_data->sense_left);
+               sense_map = SLIST_FIRST(&scb_data->sense_maps);
+               sense_data = sense_map->vaddr + offset;
+               sense_busaddr = sense_map->physaddr + offset;
+       } else {
+               sense_map = malloc(sizeof(*sense_map), M_DEVBUF, M_NOWAIT);
+
+               if (sense_map == NULL)
+                       return;
+
+               /* Allocate the next batch of sense buffers */
+               if (ahd_dmamem_alloc(ahd, scb_data->sense_dmat,
+                                    (void **)&sense_map->vaddr,
+                                    BUS_DMA_NOWAIT, &sense_map->dmamap) != 0) {
+                       free(sense_map, M_DEVBUF);
+                       return;
+               }
+
+               SLIST_INSERT_HEAD(&scb_data->sense_maps, sense_map, links);
+
+               ahd_dmamap_load(ahd, scb_data->sense_dmat, sense_map->dmamap,
+                               sense_map->vaddr, PAGE_SIZE, ahd_dmamap_cb,
+                               &sense_map->physaddr, /*flags*/0);
+
+               sense_data = sense_map->vaddr;
+               sense_busaddr = sense_map->physaddr;
+               scb_data->sense_left = PAGE_SIZE / AHD_SENSE_BUFSIZE;
+#ifdef AHD_DEBUG
+               if (ahd_debug & AHD_SHOW_MEMORY)
+                       printf("Mapped sense data\n");
+#endif
+       }
+
+       newcount = MIN(scb_data->sense_left, scb_data->scbs_left);
+       newcount = MIN(newcount, scb_data->sgs_left);
+       newcount = MIN(newcount, (AHD_SCB_MAX_ALLOC - scb_data->numscbs));
+       scb_data->sense_left -= newcount;
+       scb_data->scbs_left -= newcount;
+       scb_data->sgs_left -= newcount;
+       for (i = 0; i < newcount; i++) {
+               struct scb_platform_data *pdata;
+#ifndef __linux__
+               int error;
+#endif
+               next_scb = (struct scb *)malloc(sizeof(*next_scb),
+                                               M_DEVBUF, M_NOWAIT);
+               if (next_scb == NULL)
+                       break;
+
+               pdata = (struct scb_platform_data *)malloc(sizeof(*pdata),
+                                                          M_DEVBUF, M_NOWAIT);
+               if (pdata == NULL) {
+                       free(next_scb, M_DEVBUF);
+                       break;
+               }
+               next_scb->platform_data = pdata;
+               next_scb->hscb_map = hscb_map;
+               next_scb->sg_map = sg_map;
+               next_scb->sense_map = sense_map;
+               next_scb->sg_list = segs;
+               next_scb->sense_data = sense_data;
+               next_scb->sense_busaddr = sense_busaddr;
+               next_scb->hscb = hscb;
+               hscb->hscb_busaddr = ahd_htole32(hscb_busaddr);
+
+               /*
+                * The sequencer always starts with the second entry.
+                * The first entry is embedded in the scb.
+                */
+               next_scb->sg_list_busaddr = sg_busaddr;
+               if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+                       next_scb->sg_list_busaddr
+                           += sizeof(struct ahd_dma64_seg);
+               else
+                       next_scb->sg_list_busaddr += sizeof(struct ahd_dma_seg);
+               next_scb->ahd_softc = ahd;
+               next_scb->flags = SCB_FREE;
+#ifndef __linux__
+               error = ahd_dmamap_create(ahd, ahd->buffer_dmat, /*flags*/0,
+                                         &next_scb->dmamap);
+               if (error != 0) {
+                       free(next_scb, M_DEVBUF);
+                       free(pdata, M_DEVBUF);
+                       break;
+               }
+#endif
+               next_scb->hscb->tag = ahd_htole16(scb_data->numscbs);
+
+               SLIST_INSERT_HEAD(&scb_data->free_scbs,
+                                 next_scb, links.sle);
+               hscb++;
+               hscb_busaddr += sizeof(*hscb);
+               segs += ahd_sglist_size(ahd);
+               sg_busaddr += ahd_sglist_size(ahd);
+               sense_data += AHD_SENSE_BUFSIZE;
+               sense_busaddr += AHD_SENSE_BUFSIZE;
+               scb_data->numscbs++;
+       }
+}
+
+void
+ahd_controller_info(struct ahd_softc *ahd, char *buf)
+{
+       const char *speed;
+       const char *type;
+       int len;
+
+       len = sprintf(buf, "%s: ", ahd_chip_names[ahd->chip & AHD_CHIPID_MASK]);
+       buf += len;
+
+       speed = "Ultra320 ";
+       if ((ahd->features & AHD_WIDE) != 0) {
+               type = "Wide";
+       } else {
+               type = "Single";
+       }
+       len = sprintf(buf, "%s%sChannel %c, SCSI Id=%d, ",
+                     speed, type, ahd->channel, ahd->our_id);
+       buf += len;
+
+       sprintf(buf, "%s, %d SCBs", ahd->bus_description,
+               ahd->scb_data.maxhscbs);
+}
+
+static const char *channel_strings[] = {
+       "Primary Low",
+       "Primary High",
+       "Secondary Low", 
+       "Secondary High"
+};
+
+static const char *termstat_strings[] = {
+       "Terminated Correctly",
+       "Over Terminated",
+       "Under Terminated",
+       "Not Configured"
+};
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+ahd_init(struct ahd_softc *ahd)
+{
+       size_t   driver_data_size;
+       int      i;
+       int      error;
+       int      wait;
+       u_int    warn_user;
+       uint8_t  current_sensing;
+       uint8_t  fstat;
+
+       AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+
+       /*
+        * Verify that the compiler hasn't over-agressively
+        * padded important structures.
+        */
+       if (sizeof(struct hardware_scb) != 64)
+               panic("Hardware SCB size is incorrect");
+
+#ifdef AHD_DEBUG_SEQUENCER
+       ahd->flags |= AHD_SEQUENCER_DEBUG;
+#endif
+
+       /*
+        * Default to allowing initiator operations.
+        */
+       ahd->flags |= AHD_INITIATORROLE;
+
+       /*
+        * Only allow target mode features if this unit has them enabled.
+        */
+       if ((AHD_TMODE_ENABLE & (0x1 << ahd->unit)) == 0)
+               ahd->features &= ~AHD_TARGETMODE;
+
+#ifndef __linux__
+       /* DMA tag for mapping buffers into device visible space. */
+       if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+                              /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+                              /*lowaddr*/BUS_SPACE_MAXADDR,
+                              /*highaddr*/BUS_SPACE_MAXADDR,
+                              /*filter*/NULL, /*filterarg*/NULL,
+                              /*maxsize*/MAXBSIZE, /*nsegments*/AHD_NSEG,
+                              /*maxsegsz*/AHD_MAXTRANSFER_SIZE,
+                              /*flags*/BUS_DMA_ALLOCNOW,
+                              &ahd->buffer_dmat) != 0) {
+               return (ENOMEM);
+       }
+#endif
+
+       ahd->init_level++;
+
+       /*
+        * DMA tag for our command fifos and other data in system memory
+        * the card's sequencer must be able to access.  For initiator
+        * roles, we need to allocate space for the qoutfifo.  When providing
+        * for the target mode role, we must additionally provide space for
+        * the incoming target command fifo.
+        */
+       driver_data_size = AHD_SCB_MAX * sizeof(uint16_t);
+       if ((ahd->features & AHD_TARGETMODE) != 0)
+               driver_data_size += AHD_TMODE_CMDS * sizeof(struct target_cmd);
+       if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0)
+               driver_data_size += PKT_OVERRUN_BUFSIZE;
+       if (ahd_dma_tag_create(ahd, ahd->parent_dmat, /*alignment*/1,
+                              /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+                              /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+                              /*highaddr*/BUS_SPACE_MAXADDR,
+                              /*filter*/NULL, /*filterarg*/NULL,
+                              driver_data_size,
+                              /*nsegments*/1,
+                              /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+                              /*flags*/0, &ahd->shared_data_dmat) != 0) {
+               return (ENOMEM);
+       }
+
+       ahd->init_level++;
+
+       /* Allocation of driver data */
+       if (ahd_dmamem_alloc(ahd, ahd->shared_data_dmat,
+                            (void **)&ahd->qoutfifo,
+                            BUS_DMA_NOWAIT, &ahd->shared_data_dmamap) != 0) {
+               return (ENOMEM);
+       }
+
+       ahd->init_level++;
+
+       /* And permanently map it in */
+       ahd_dmamap_load(ahd, ahd->shared_data_dmat, ahd->shared_data_dmamap,
+                       ahd->qoutfifo, driver_data_size, ahd_dmamap_cb,
+                       &ahd->shared_data_busaddr, /*flags*/0);
+
+       if ((ahd->features & AHD_TARGETMODE) != 0) {
+               /* XXX sequencer assumes qoutfifo is first. */
+               ahd->targetcmds = (struct target_cmd *)ahd->qoutfifo;
+               ahd->qoutfifo = (uint16_t *)&ahd->targetcmds[AHD_TMODE_CMDS];
+       }
+
+       if ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0)
+               ahd->overrun_buf = (uint8_t *)&ahd->qoutfifo[AHD_SCB_MAX];
+
+       ahd->init_level++;
+
+       /* Allocate SCB data now that buffer_dmat is initialized */
+       if (ahd_init_scbdata(ahd) != 0)
+               return (ENOMEM);
+
+       if ((ahd->flags & AHD_INITIATORROLE) == 0)
+               ahd->flags &= ~AHD_RESET_BUS_A;
+
+       ahd_chip_init(ahd);
+
+       AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+
+       /*
+        * Wait for up to 500ms for our transceivers
+        * to settle.  If the adapter does not have
+        * a cable attached, the tranceivers may
+        * never settle, so don't complain if we
+        * fail here.
+        */
+       for (wait = 10000;
+            (ahd_inb(ahd, SBLKCTL) & (ENAB40|ENAB20)) == 0 && wait;
+            wait--)
+               ahd_delay(100);
+       if ((ahd->flags & AHD_CURRENT_SENSING) == 0)
+               goto init_done;
+
+       /*
+        * Verify termination based on current draw and
+        * warn user if the bus is over/under terminated.
+        */
+       error = ahd_write_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL,
+                                  CURSENSE_ENB);
+       if (error != 0) {
+               printf("%s: current sensing timeout 1\n", ahd_name(ahd));
+               goto init_done;
+       }
+       for (i = 20, fstat = FLX_FSTAT_BUSY;
+            (fstat & FLX_FSTAT_BUSY) != 0 && i; i--) {
+               error = ahd_read_flexport(ahd, FLXADDR_FLEXSTAT, &fstat);
+               if (error != 0) {
+                       printf("%s: current sensing timeout 2\n",
+                              ahd_name(ahd));
+                       goto init_done;
+               }
+       }
+       if (i == 0) {
+               printf("%s: Timedout during current-sensing test\n",
+                      ahd_name(ahd));
+               goto init_done;
+       }
+
+       /* Latch Current Sensing status. */
+       error = ahd_read_flexport(ahd, FLXADDR_CURRENT_STAT, &current_sensing);
+       if (error != 0) {
+               printf("%s: current sensing timeout 3\n", ahd_name(ahd));
+               goto init_done;
+       }
+
+       /* Diable current sensing. */
+       ahd_write_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL, 0);
+
+#ifdef AHD_DEBUG
+       if ((ahd_debug & AHD_SHOW_TERMCTL) != 0) {
+               printf("%s: current_sensing == 0x%x\n",
+                      ahd_name(ahd), current_sensing);
+       }
+#endif
+       warn_user = 0;
+       for (i = 0; i < 4; i++, current_sensing >>= FLX_CSTAT_SHIFT) {
+               u_int term_stat;
+
+               term_stat = (current_sensing & FLX_CSTAT_MASK);
+               switch (term_stat) {
+               case FLX_CSTAT_OVER:
+               case FLX_CSTAT_UNDER:
+                       warn_user++;
+               case FLX_CSTAT_INVALID:
+               case FLX_CSTAT_OKAY:
+                       if (warn_user == 0 && bootverbose == 0)
+                               break;
+                       printf("%s: %s Channel %s\n", ahd_name(ahd),
+                              channel_strings[i], termstat_strings[term_stat]);
+                       break;
+               }
+       }
+       if (warn_user) {
+               printf("%s: WARNING. Termination is not configured correctly.\n"
+                      "%s: WARNING. SCSI bus operations may FAIL.\n",
+                      ahd_name(ahd), ahd_name(ahd));
+       }
+init_done:
+       ahd_restart(ahd);
+       return (0);
+}
+
+/*
+ * (Re)initialize chip state after a chip reset.
+ */
+static void
+ahd_chip_init(struct ahd_softc *ahd)
+{
+       uint32_t busaddr;
+       u_int    sxfrctl1;
+       u_int    scsiseq_template;
+       u_int    i;
+       u_int    target;
+
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       /*
+        * Take the LED out of diagnostic mode
+        */
+       ahd_outb(ahd, SBLKCTL, ahd_inb(ahd, SBLKCTL) & ~(DIAGLEDEN|DIAGLEDON));
+
+       /* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1. */
+       ahd_outb(ahd, IOWNID, ahd->our_id);
+       ahd_outb(ahd, TOWNID, ahd->our_id);
+       sxfrctl1 = (ahd->flags & AHD_TERM_ENB_A) != 0 ? STPWEN : 0;
+       sxfrctl1 |= (ahd->flags & AHD_SPCHK_ENB_A) != 0 ? ENSPCHK : 0;
+       if ((ahd->bugs & AHD_LONG_SETIMO_BUG)
+        && (ahd->seltime != STIMESEL_MIN)) {
+               /*
+                * The selection timer duration is twice as long
+                * as it should be.  Halve it by adding "1" to
+                * the user specified setting.
+                */
+               sxfrctl1 |= ahd->seltime + STIMESEL_BUG_ADJ;
+       } else {
+               sxfrctl1 |= ahd->seltime;
+       }
+               
+       ahd_outb(ahd, SXFRCTL0, DFON);
+       ahd_outb(ahd, SXFRCTL1, sxfrctl1|ahd->seltime|ENSTIMER|ACTNEGEN);
+       ahd_outb(ahd, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+
+       /* Initialize mode specific S/G state. */
+       for (i = 0; i < 2; i++) {
+               ahd_set_modes(ahd, AHD_MODE_DFF0 + i, AHD_MODE_DFF0 + i);
+               ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR);
+               ahd_outw(ahd, LONGJMP_SCB, SCB_LIST_NULL);
+               ahd_outb(ahd, SG_STATE, 0);
+               ahd_outb(ahd, CLRSEQINTSRC, 0xFF);
+               ahd_outb(ahd, SEQIMODE,
+                        ENSAVEPTRS|ENCFG4DATA|ENCFG4ISTAT
+                       |ENCFG4TSTAT|ENCFG4ICMD|ENCFG4TCMD);
+       }
+
+       ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+       ahd_outb(ahd, DSCOMMAND0, ahd_inb(ahd, DSCOMMAND0)|MPARCKEN|CACHETHEN);
+       ahd_outb(ahd, DFF_THRSH, RD_DFTHRSH_75|WR_DFTHRSH_75);
+       ahd_outb(ahd, SIMODE0, ENIOERR|ENOVERRUN);
+       ahd_outb(ahd, SIMODE3, ENNTRAMPERR|ENOSRAMPERR);
+       ahd_outb(ahd, OPTIONMODE, AUTOACKEN|BUSFREEREV|AUTO_MSGOUT_DE);
+       if ((ahd->chip & AHD_BUS_MASK) == AHD_PCIX)
+               /*
+                * Do not issue a target abort when a split completion
+                * error occurs.  Let our PCIX interrupt handler deal
+                * with it instead. H2A4 Razor #625
+                */
+               ahd_outb(ahd, PCIXCTL, ahd_inb(ahd, PCIXCTL) | SPLTSTADIS);
+
+       /*
+        * Tweak IOCELL settings.
+        */
+       if ((ahd->flags & AHD_CPQ_BOARD) != 0) {
+               for (i = 0; i < NUMDSPS; i++) {
+                       ahd_outb(ahd, DSPSELECT, i);
+                       ahd_outb(ahd, WRTBIASCTL, WRTBIASCTL_CPQ_DEFAULT);
+               }
+       }
+       ahd_setup_iocell_workaround(ahd);
+
+       /*
+        * Enable LQI Manager interrupts.
+        */
+       ahd_outb(ahd, LQIMODE1, ENLQIPHASE_LQ|ENLQIPHASE_NLQ|ENLIQABORT
+                             | ENLQICRCI_LQ|ENLQICRCI_NLQ|ENLQIBADLQI
+                             | ENLQIOVERI_LQ|ENLQIOVERI_NLQ);
+       ahd_outb(ahd, LQOMODE0, ENLQOATNLQ|ENLQOATNPKT|ENLQOTCRC);
+       /*
+        * An interrupt from LQOBUSFREE is made redundant by the
+        * BUSFREE interrupt.  We choose to have the sequencer catch
+        * LQOPHCHGINPKT errors manually for the command phase at the
+        * start of a packetized selection case.
+       ahd_outb(ahd, LQOMODE1, ENLQOBUSFREE|ENLQOPHACHGINPKT);
+        */
+       ahd_outb(ahd, LQOMODE1, 0);
+
+       /*
+        * Setup sequencer interrupt handler.
+        */
+       ahd_outw(ahd, INTVEC1_ADDR, ahd_resolve_seqaddr(ahd, LABEL_seq_isr));
+
+       /*
+        * Setup SCB Offset registers.
+        */
+       ahd_outb(ahd, LUNPTR, offsetof(struct hardware_scb, lun));
+       ahd_outb(ahd, CMDLENPTR, offsetof(struct hardware_scb, cdb_len));
+       ahd_outb(ahd, ATTRPTR,
+                offsetof(struct hardware_scb, task_attribute_nonpkt_tag));
+       ahd_outb(ahd, FLAGPTR, offsetof(struct hardware_scb, task_management));
+       ahd_outb(ahd, CMDPTR, offsetof(struct hardware_scb,
+                                      shared_data.idata.cdb));
+       ahd_outb(ahd, QNEXTPTR,
+                offsetof(struct hardware_scb, next_hscb_busaddr));
+       ahd_outb(ahd, ABRTBITPTR, MK_MESSAGE_BIT_OFFSET);
+       ahd_outb(ahd, ABRTBYTEPTR, offsetof(struct hardware_scb, control));
+       ahd_outb(ahd, LUNLEN, sizeof(ahd->next_queued_scb->hscb->lun) - 1);
+       ahd_outb(ahd, CDBLIMIT, SCB_CDB_LEN_PTR - 1);
+       ahd_outb(ahd, MAXCMD, 0xFF);
+       ahd_outb(ahd, SCBAUTOPTR,
+                AUSCBPTR_EN | offsetof(struct hardware_scb, tag));
+
+       /* We haven't been enabled for target mode yet. */
+       ahd_outb(ahd, MULTARGID, 0);
+       ahd_outb(ahd, MULTARGID + 1, 0);
+
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       /*
+        * Clear the spare bytes in the neg table to avoid
+        * spurious parity errors.
+        */
+       for (target = 0; target < AHD_NUM_TARGETS; target++) {
+
+               ahd_outb(ahd, NEGOADDR, target);
+               ahd_outb(ahd, ANNEXCOL, AHD_ANNEXCOL_PRECOMP);
+               for (i = 0; i < AHD_NUM_ANNEXCOLS; i++)
+                       ahd_outb(ahd, ANNEXDAT, 0);
+       }
+       ahd_outb(ahd, CLRSINT3, NTRAMPERR|OSRAMPERR);
+
+       /*
+        * Always enable abort on incoming L_Qs if this feature is
+        * supported.  We use this to catch invalid SCB references.
+        */
+       if ((ahd->bugs & AHD_ABORT_LQI_BUG) == 0)
+               ahd_outb(ahd, LQCTL1, ABORTPENDING);
+       else
+               ahd_outb(ahd, LQCTL1, 0);
+
+       /* All of our queues are empty */
+       ahd->qoutfifonext = 0;
+       for (i = 0; i < AHD_QOUT_SIZE; i++)
+               ahd->qoutfifo[i] = SCB_LIST_NULL_LE;
+       ahd_sync_qoutfifo(ahd, BUS_DMASYNC_PREREAD);
+
+       ahd->qinfifonext = 0;
+       for (i = 0; i < AHD_QIN_SIZE; i++)
+               ahd->qinfifo[i] = SCB_LIST_NULL;
+
+       if ((ahd->features & AHD_TARGETMODE) != 0) {
+               /* All target command blocks start out invalid. */
+               for (i = 0; i < AHD_TMODE_CMDS; i++)
+                       ahd->targetcmds[i].cmd_valid = 0;
+               ahd_sync_tqinfifo(ahd, BUS_DMASYNC_PREREAD);
+               ahd->tqinfifonext = 1;
+               ahd_outb(ahd, KERNEL_TQINPOS, ahd->tqinfifonext - 1);
+               ahd_outb(ahd, TQINPOS, ahd->tqinfifonext);
+       }
+
+       /* Initialize Scratch Ram. */
+       ahd_outb(ahd, SEQ_FLAGS, 0);
+       ahd_outb(ahd, SEQ_FLAGS2, 0);
+
+       /* We don't have any waiting selections */
+       ahd_outw(ahd, WAITING_TID_HEAD, SCB_LIST_NULL);
+       ahd_outw(ahd, WAITING_TID_TAIL, SCB_LIST_NULL);
+       for (i = 0; i < AHD_NUM_TARGETS; i++)
+               ahd_outw(ahd, WAITING_SCB_TAILS + (2 * i), SCB_LIST_NULL);
+
+       /*
+        * Nobody is waiting to be DMAed into the QOUTFIFO.
+        */
+       ahd_outw(ahd, COMPLETE_SCB_HEAD, SCB_LIST_NULL);
+       ahd_outw(ahd, COMPLETE_SCB_DMAINPROG_HEAD, SCB_LIST_NULL);
+       ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD, SCB_LIST_NULL);
+
+       /*
+        * The Freeze Count is 0.
+        */
+       ahd_outw(ahd, QFREEZE_COUNT, 0);
+
+       /*
+        * Tell the sequencer where it can find our arrays in memory.
+        */
+       busaddr = ahd->shared_data_busaddr;
+       ahd_outb(ahd, SHARED_DATA_ADDR, busaddr & 0xFF);
+       ahd_outb(ahd, SHARED_DATA_ADDR + 1, (busaddr >> 8) & 0xFF);
+       ahd_outb(ahd, SHARED_DATA_ADDR + 2, (busaddr >> 16) & 0xFF);
+       ahd_outb(ahd, SHARED_DATA_ADDR + 3, (busaddr >> 24) & 0xFF);
+       ahd_outb(ahd, QOUTFIFO_NEXT_ADDR, busaddr & 0xFF);
+       ahd_outb(ahd, QOUTFIFO_NEXT_ADDR + 1, (busaddr >> 8) & 0xFF);
+       ahd_outb(ahd, QOUTFIFO_NEXT_ADDR + 2, (busaddr >> 16) & 0xFF);
+       ahd_outb(ahd, QOUTFIFO_NEXT_ADDR + 3, (busaddr >> 24) & 0xFF);
+
+       /*
+        * Setup the allowed SCSI Sequences based on operational mode.
+        * If we are a target, we'll enable select in operations once
+        * we've had a lun enabled.
+        */
+       scsiseq_template = ENAUTOATNP;
+       if ((ahd->flags & AHD_INITIATORROLE) != 0)
+               scsiseq_template |= ENRSELI;
+       ahd_outb(ahd, SCSISEQ_TEMPLATE, scsiseq_template);
+
+       /* There are no busy SCBs yet. */
+       for (target = 0; target < AHD_NUM_TARGETS; target++) {
+               int lun;
+
+               for (lun = 0; lun < AHD_NUM_LUNS_NONPKT; lun++)
+                       ahd_unbusy_tcl(ahd, BUILD_TCL_RAW(target, 'A', lun));
+       }
+
+       /*
+        * Always enable abort on incoming L_Qs if this feature is
+        * supported.  We use this to catch invalid SCB references.
+        */
+       if ((ahd->bugs & AHD_ABORT_LQI_BUG) == 0)
+               ahd_outb(ahd, LQCTL1, ABORTPENDING);
+       else
+               ahd_outb(ahd, LQCTL1, 0);
+
+       /*
+        * Initialize the group code to command length table.
+        * Vendor Unique codes are set to 0 so we only capture
+        * the first byte of the cdb.  These can be overridden
+        * when target mode is enabled.
+        */
+       ahd_outb(ahd, CMDSIZE_TABLE, 5);
+       ahd_outb(ahd, CMDSIZE_TABLE + 1, 9);
+       ahd_outb(ahd, CMDSIZE_TABLE + 2, 9);
+       ahd_outb(ahd, CMDSIZE_TABLE + 3, 0);
+       ahd_outb(ahd, CMDSIZE_TABLE + 4, 15);
+       ahd_outb(ahd, CMDSIZE_TABLE + 5, 11);
+       ahd_outb(ahd, CMDSIZE_TABLE + 6, 0);
+       ahd_outb(ahd, CMDSIZE_TABLE + 7, 0);
+               
+       /* Tell the sequencer of our initial queue positions */
+       ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+       ahd_outb(ahd, QOFF_CTLSTA, SCB_QSIZE_512);
+       ahd->qinfifonext = 0;
+       ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+       ahd_set_hescb_qoff(ahd, 0);
+       ahd_set_snscb_qoff(ahd, 0);
+       ahd_set_sescb_qoff(ahd, 0);
+       ahd_set_sdscb_qoff(ahd, 0);
+
+       /*
+        * Tell the sequencer which SCB will be the next one it receives.
+        */
+       busaddr = ahd_le32toh(ahd->next_queued_scb->hscb->hscb_busaddr);
+       ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 0, busaddr & 0xFF);
+       ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 1, (busaddr >> 8) & 0xFF);
+       ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 2, (busaddr >> 16) & 0xFF);
+       ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 3, (busaddr >> 24) & 0xFF);
+       ahd_loadseq(ahd);
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+}
+
+/*
+ * Setup default device and controller settings.
+ * This should only be called if our probe has
+ * determined that no configuration data is available.
+ */
+int
+ahd_default_config(struct ahd_softc *ahd)
+{
+       int     targ;
+
+       ahd->our_id = 7;
+
+       /*
+        * Allocate a tstate to house information for our
+        * initiator presence on the bus as well as the user
+        * data for any target mode initiator.
+        */
+       if (ahd_alloc_tstate(ahd, ahd->our_id, 'A') == NULL) {
+               printf("%s: unable to allocate ahd_tmode_tstate.  "
+                      "Failing attach\n", ahd_name(ahd));
+               return (ENOMEM);
+       }
+
+       for (targ = 0; targ < AHD_NUM_TARGETS; targ++) {
+               struct   ahd_devinfo devinfo;
+               struct   ahd_initiator_tinfo *tinfo;
+               struct   ahd_tmode_tstate *tstate;
+               uint16_t target_mask;
+
+               TAILQ_INIT(&ahd->untagged_queues[targ]);
+               tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+                                           targ, &tstate);
+               /*
+                * We support SPC2 and SPI4.
+                */
+               tinfo->user.protocol_version = 4;
+               tinfo->user.transport_version = 4;
+
+               target_mask = 0x01 << targ;
+               ahd->user_discenable |= target_mask;
+               tstate->discenable |= target_mask;
+               ahd->user_tagenable |= target_mask;
+#ifdef AHD_FORCE_160
+               tinfo->user.period = AHD_SYNCRATE_DT;
+#else
+               tinfo->user.period = AHD_SYNCRATE_160;
+#endif
+               tinfo->user.offset= ~0;
+               tinfo->user.ppr_options = MSG_EXT_PPR_RD_STRM
+                                       | MSG_EXT_PPR_WR_FLOW
+                                       | MSG_EXT_PPR_IU_REQ
+                                       | MSG_EXT_PPR_QAS_REQ
+                                       | MSG_EXT_PPR_DT_REQ;
+
+               tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+
+               /*
+                * Start out Async/Narrow/Untagged and with
+                * conservative protocol support.
+                */
+               tinfo->goal.protocol_version = 2;
+               tinfo->goal.transport_version = 2;
+               tinfo->curr.protocol_version = 2;
+               tinfo->curr.transport_version = 2;
+               ahd_compile_devinfo(&devinfo, ahd->our_id,
+                                   targ, CAM_LUN_WILDCARD,
+                                   'A', ROLE_INITIATOR);
+               tstate->tagenable &= ~target_mask;
+               ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+                             AHD_TRANS_CUR|AHD_TRANS_GOAL, /*paused*/TRUE);
+               ahd_set_syncrate(ahd, &devinfo, /*period*/0, /*offset*/0,
+                                /*ppr_options*/0, AHD_TRANS_CUR|AHD_TRANS_GOAL,
+                                /*paused*/TRUE);
+               /*
+                * The neg table must be initialized even if the
+                * new settings above are the same as those from
+                * when our xfer info data structures were allocated
+                * and initialized.
+                */
+               ahd_update_neg_table(ahd, &devinfo, &tinfo->curr);
+       }
+       return (0);
+}
+
+/*
+ * Parse device configuration information.
+ */
+int
+ahd_parse_cfgdata(struct ahd_softc *ahd, struct seeprom_config *sc)
+{
+       int targ;
+       int max_targ;
+
+       max_targ = sc->max_targets & CFMAXTARG;
+       ahd->our_id = sc->brtime_id & CFSCSIID;
+
+       /*
+        * Allocate a tstate to house information for our
+        * initiator presence on the bus as well as the user
+        * data for any target mode initiator.
+        */
+       if (ahd_alloc_tstate(ahd, ahd->our_id, 'A') == NULL) {
+               printf("%s: unable to allocate ahd_tmode_tstate.  "
+                      "Failing attach\n", ahd_name(ahd));
+               return (ENOMEM);
+       }
+
+       for (targ = 0; targ < max_targ; targ++) {
+               struct   ahd_devinfo devinfo;
+               struct   ahd_initiator_tinfo *tinfo;
+               struct   ahd_transinfo *user_tinfo;
+               struct   ahd_tmode_tstate *tstate;
+               uint16_t target_mask;
+
+               tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id,
+                                           targ, &tstate);
+               user_tinfo = &tinfo->user;
+
+               /*
+                * We support SPC2 and SPI4.
+                */
+               tinfo->user.protocol_version = 4;
+               tinfo->user.transport_version = 4;
+
+               target_mask = 0x01 << targ;
+               ahd->user_discenable &= ~target_mask;
+               tstate->discenable &= ~target_mask;
+               ahd->user_tagenable &= ~target_mask;
+               if (sc->device_flags[targ] & CFDISC) {
+                       tstate->discenable |= target_mask;
+                       ahd->user_discenable |= target_mask;
+                       ahd->user_tagenable |= target_mask;
+               } else {
+                       /*
+                        * Cannot be packetized without disconnection.
+                        */
+                       sc->device_flags[targ] &= ~CFPACKETIZED;
+               }
+
+               user_tinfo->ppr_options = 0;
+               user_tinfo->period = (sc->device_flags[targ] & CFXFER);
+               if (user_tinfo->period < CFXFER_ASYNC) {
+                       if (user_tinfo->period <= AHD_PERIOD_10MHz)
+                               user_tinfo->ppr_options |= MSG_EXT_PPR_DT_REQ;
+                       user_tinfo->offset = MAX_OFFSET;
+               } else  {
+                       user_tinfo->offset = 0;
+                       user_tinfo->period = AHD_PERIOD_ASYNC;
+               }
+#ifdef AHD_FORCE_160
+               if (user_tinfo->period <= AHD_SYNCRATE_160)
+                       user_tinfo->period = AHD_SYNCRATE_DT;
+#endif
+
+               if ((sc->device_flags[targ] & CFPACKETIZED) != 0)
+                       user_tinfo->ppr_options |= MSG_EXT_PPR_RD_STRM
+                                               |  MSG_EXT_PPR_WR_FLOW
+                                               |  MSG_EXT_PPR_IU_REQ;
+
+               if ((sc->device_flags[targ] & CFQAS) != 0)
+                       user_tinfo->ppr_options |= MSG_EXT_PPR_QAS_REQ;
+
+               if ((sc->device_flags[targ] & CFWIDEB) != 0)
+                       user_tinfo->width = MSG_EXT_WDTR_BUS_16_BIT;
+               else
+                       user_tinfo->width = MSG_EXT_WDTR_BUS_8_BIT;
+#ifdef AHD_DEBUG
+               if ((ahd_debug & AHD_SHOW_MISC) != 0)
+                       printf("(%d): %x:%x:%x:%x\n", targ, user_tinfo->width,
+                              user_tinfo->period, user_tinfo->offset,
+                              user_tinfo->ppr_options);
+#endif
+               /*
+                * Start out Async/Narrow/Untagged and with
+                * conservative protocol support.
+                */
+               tstate->tagenable &= ~target_mask;
+               tinfo->goal.protocol_version = 2;
+               tinfo->goal.transport_version = 2;
+               tinfo->curr.protocol_version = 2;
+               tinfo->curr.transport_version = 2;
+               ahd_compile_devinfo(&devinfo, ahd->our_id,
+                                   targ, CAM_LUN_WILDCARD,
+                                   'A', ROLE_INITIATOR);
+               ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+                             AHD_TRANS_CUR|AHD_TRANS_GOAL, /*paused*/TRUE);
+               ahd_set_syncrate(ahd, &devinfo, /*period*/0, /*offset*/0,
+                                /*ppr_options*/0, AHD_TRANS_CUR|AHD_TRANS_GOAL,
+                                /*paused*/TRUE);
+               /*
+                * The neg table must be initialized even if the
+                * new settings above are the same as those from
+                * when our xfer info data structures were allocated
+                * and initialized.
+                */
+               ahd_update_neg_table(ahd, &devinfo, &tinfo->curr);
+       }
+
+       ahd->flags &= ~AHD_SPCHK_ENB_A;
+       if (sc->bios_control & CFSPARITY)
+               ahd->flags |= AHD_SPCHK_ENB_A;
+
+       ahd->flags &= ~AHD_RESET_BUS_A;
+       if (sc->bios_control & CFRESETB)
+               ahd->flags |= AHD_RESET_BUS_A;
+
+       ahd->flags &= ~AHD_EXTENDED_TRANS_A;
+       if (sc->bios_control & CFEXTEND)
+               ahd->flags |= AHD_EXTENDED_TRANS_A;
+
+       ahd->flags &= ~AHD_BIOS_ENABLED;
+       if ((sc->bios_control & CFBIOSSTATE) == CFBS_ENABLED)
+               ahd->flags |= AHD_BIOS_ENABLED;
+
+       ahd->flags &= ~AHD_STPWLEVEL_A;
+       if ((sc->adapter_control & CFSTPWLEVEL) != 0)
+               ahd->flags |= AHD_STPWLEVEL_A;
+
+       return (0);
+}
+
+void
+ahd_intr_enable(struct ahd_softc *ahd, int enable)
+{
+       u_int hcntrl;
+
+       hcntrl = ahd_inb(ahd, HCNTRL);
+       hcntrl &= ~INTEN;
+       ahd->pause &= ~INTEN;
+       ahd->unpause &= ~INTEN;
+       if (enable) {
+               hcntrl |= INTEN;
+               ahd->pause |= INTEN;
+               ahd->unpause |= INTEN;
+       }
+       ahd_outb(ahd, HCNTRL, hcntrl);
+}
+
+/*
+ * Ensure that the card is paused in a location
+ * outside of all critical sections and that all
+ * pending work is completed prior to returning.
+ * This routine should only be called from outside
+ * an interrupt context.
+ */
+void
+ahd_pause_and_flushwork(struct ahd_softc *ahd)
+{
+       int intstat;
+       int maxloops;
+
+       maxloops = 1000;
+       ahd->flags |= AHD_ALL_INTERRUPTS;
+       intstat = 0;
+       do {
+               ahd_intr(ahd);
+               ahd_pause(ahd);
+               ahd_clear_critical_section(ahd);
+               if (intstat == 0xFF && (ahd->features & AHD_REMOVABLE) != 0)
+                       break;
+               maxloops--;
+       } while (((intstat = ahd_inb(ahd, INTSTAT)) & INT_PEND) && --maxloops);
+       if (maxloops == 0) {
+               printf("Infinite interrupt loop, INTSTAT = %x",
+                     ahd_inb(ahd, INTSTAT));
+       }
+       ahd_platform_flushwork(ahd);
+       ahd->flags &= ~AHD_ALL_INTERRUPTS;
+}
+
+int
+ahd_suspend(struct ahd_softc *ahd)
+{
+#if 0
+       uint8_t *ptr;
+       int      i;
+
+       ahd_pause_and_flushwork(ahd);
+
+       if (LIST_FIRST(&ahd->pending_scbs) != NULL)
+               return (EBUSY);
+
+#if AHD_TARGET_MODE
+       /*
+        * XXX What about ATIOs that have not yet been serviced?
+        * Perhaps we should just refuse to be suspended if we
+        * are acting in a target role.
+        */
+       if (ahd->pending_device != NULL)
+               return (EBUSY);
+#endif
+
+       /* Save volatile registers */
+       ahd->suspend_state.channel[0].scsiseq = ahd_inb(ahd, SCSISEQ0);
+       ahd->suspend_state.channel[0].sxfrctl0 = ahd_inb(ahd, SXFRCTL0);
+       ahd->suspend_state.channel[0].sxfrctl1 = ahd_inb(ahd, SXFRCTL1);
+       ahd->suspend_state.channel[0].simode0 = ahd_inb(ahd, SIMODE0);
+       ahd->suspend_state.channel[0].simode1 = ahd_inb(ahd, SIMODE1);
+       ahd->suspend_state.channel[0].seltimer = ahd_inb(ahd, SELTIMER);
+       ahd->suspend_state.channel[0].seqctl = ahd_inb(ahd, SEQCTL0);
+       ahd->suspend_state.dscommand0 = ahd_inb(ahd, DSCOMMAND0);
+       ahd->suspend_state.dspcistatus = ahd_inb(ahd, DSPCISTATUS);
+
+       if ((ahd->features & AHD_DT) != 0) {
+               u_int sfunct;
+
+               sfunct = ahd_inb(ahd, SFUNCT) & ~ALT_MODE;
+               ahd_outb(ahd, SFUNCT, sfunct | ALT_MODE);
+               ahd->suspend_state.optionmode = ahd_inb(ahd, OPTIONMODE);
+               ahd_outb(ahd, SFUNCT, sfunct);
+               ahd->suspend_state.crccontrol1 = ahd_inb(ahd, CRCCONTROL1);
+       }
+
+       if ((ahd->features & AHD_MULTI_FUNC) != 0)
+               ahd->suspend_state.scbbaddr = ahd_inb(ahd, SCBBADDR);
+
+       if ((ahd->features & AHD_ULTRA2) != 0)
+               ahd->suspend_state.dff_thrsh = ahd_inb(ahd, DFF_THRSH);
+
+       ptr = ahd->suspend_state.scratch_ram;
+       for (i = 0; i < 64; i++)
+               *ptr++ = ahd_inb(ahd, SRAM_BASE + i);
+
+       if ((ahd->features & AHD_MORE_SRAM) != 0) {
+               for (i = 0; i < 16; i++)
+                       *ptr++ = ahd_inb(ahd, TARG_OFFSET + i);
+       }
+
+       ptr = ahd->suspend_state.btt;
+       for (i = 0;i < AHD_NUM_TARGETS; i++) {
+               int j;
+
+               for (j = 0;j < AHD_NUM_LUNS; j++) {
+                       u_int tcl;
+
+                       tcl = BUILD_TCL(i << 4, j);
+                       *ptr = ahd_find_busy_tcl(ahd, tcl);
+               }
+       }
+       ahd_shutdown(ahd);
+#endif
+       return (0);
+}
+
+int
+ahd_resume(struct ahd_softc *ahd)
+{
+#if 0
+       uint8_t *ptr;
+       int      i;
+
+       ahd_reset(ahd);
+
+       ahd_build_free_scb_list(ahd);
+
+       /* Restore volatile registers */
+       ahd_outb(ahd, SCSISEQ0, ahd->suspend_state.channel[0].scsiseq);
+       ahd_outb(ahd, SXFRCTL0, ahd->suspend_state.channel[0].sxfrctl0);
+       ahd_outb(ahd, SXFRCTL1, ahd->suspend_state.channel[0].sxfrctl1);
+       ahd_outb(ahd, SIMODE0, ahd->suspend_state.channel[0].simode0);
+       ahd_outb(ahd, SIMODE1, ahd->suspend_state.channel[0].simode1);
+       ahd_outb(ahd, SELTIMER, ahd->suspend_state.channel[0].seltimer);
+       ahd_outb(ahd, SEQCTL0, ahd->suspend_state.channel[0].seqctl);
+       if ((ahd->features & AHD_ULTRA2) != 0)
+               ahd_outb(ahd, SCSIID_ULTRA2, ahd->our_id);
+       else
+               ahd_outb(ahd, SCSIID, ahd->our_id);
+
+       ahd_outb(ahd, DSCOMMAND0, ahd->suspend_state.dscommand0);
+       ahd_outb(ahd, DSPCISTATUS, ahd->suspend_state.dspcistatus);
+
+       if ((ahd->features & AHD_DT) != 0) {
+               u_int sfunct;
+
+               sfunct = ahd_inb(ahd, SFUNCT) & ~ALT_MODE;
+               ahd_outb(ahd, SFUNCT, sfunct | ALT_MODE);
+               ahd_outb(ahd, OPTIONMODE, ahd->suspend_state.optionmode);
+               ahd_outb(ahd, SFUNCT, sfunct);
+               ahd_outb(ahd, CRCCONTROL1, ahd->suspend_state.crccontrol1);
+       }
+
+       if ((ahd->features & AHD_MULTI_FUNC) != 0)
+               ahd_outb(ahd, SCBBADDR, ahd->suspend_state.scbbaddr);
+
+       if ((ahd->features & AHD_ULTRA2) != 0)
+               ahd_outb(ahd, DFF_THRSH, ahd->suspend_state.dff_thrsh);
+
+       ptr = ahd->suspend_state.scratch_ram;
+       for (i = 0; i < 64; i++)
+               ahd_outb(ahd, SRAM_BASE + i, *ptr++);
+
+       if ((ahd->features & AHD_MORE_SRAM) != 0) {
+               for (i = 0; i < 16; i++)
+                       ahd_outb(ahd, TARG_OFFSET + i, *ptr++);
+       }
+
+       ptr = ahd->suspend_state.btt;
+       for (i = 0;i < AHD_NUM_TARGETS; i++) {
+               int j;
+
+               for (j = 0;j < AHD_NUM_LUNS; j++) {
+                       u_int tcl;
+
+                       tcl = BUILD_TCL(i << 4, j);
+                       ahd_busy_tcl(ahd, tcl, *ptr);
+               }
+       }
+#endif
+       return (0);
+}
+
+/************************** Busy Target Table *********************************/
+/*
+ * Set SCBPTR to the SCB that contains the busy
+ * table entry for TCL.  Return the offset into
+ * the SCB that contains the entry for TCL.
+ * saved_scbid is dereferenced and set to the
+ * scbid that should be restored once manipualtion
+ * of the TCL entry is complete.
+ */
+static __inline u_int
+ahd_index_busy_tcl(struct ahd_softc *ahd, u_int *saved_scbid, u_int tcl)
+{
+
+       *saved_scbid = ahd_get_scbptr(ahd);
+
+       /*
+        * Index to the SCB that contains the busy entry.
+        */
+       ahd_set_scbptr(ahd, TCL_LUN(tcl)
+                    | ((TCL_TARGET_OFFSET(tcl) & ~0x7) << 5));
+
+       /*
+        * And now calculate the SCB offset to the entry.
+        * Each entry is 2 bytes wide, hence the
+        * multiplication by 2.
+        */
+       return (((TCL_TARGET_OFFSET(tcl) & 0x7) << 1) + SCB_DISCONNECTED_LISTS);
+}
+
+/*
+ * Return the untagged transaction id for a given target/channel lun.
+ * Optionally, clear the entry.
+ */
+u_int
+ahd_find_busy_tcl(struct ahd_softc *ahd, u_int tcl)
+{
+       u_int scbid;
+       u_int scb_offset;
+       u_int saved_scbptr;
+               
+       scb_offset = ahd_index_busy_tcl(ahd, &saved_scbptr, tcl);
+       scbid = ahd_inw_scbram(ahd, scb_offset);
+       ahd_set_scbptr(ahd, saved_scbptr);
+       return (scbid);
+}
+
+void
+ahd_busy_tcl(struct ahd_softc *ahd, u_int tcl, u_int scbid)
+{
+       u_int scb_offset;
+       u_int saved_scbptr;
+               
+       scb_offset = ahd_index_busy_tcl(ahd, &saved_scbptr, tcl);
+       ahd_outw(ahd, scb_offset, scbid);
+       ahd_set_scbptr(ahd, saved_scbptr);
+}
+
+void
+ahd_set_disconnected_list(struct ahd_softc *ahd, u_int target,
+                         u_int lun, u_int scbid)
+{
+       u_int saved_scbptr;
+
+       AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+                        ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+       saved_scbptr = ahd_get_scbptr(ahd);
+       ahd_set_scbptr(ahd, lun | ((target & 0x8) << 5));
+       ahd_outw(ahd, SCB_DISCONNECTED_LISTS + ((target & 0x7) << 1), scbid);
+       ahd_set_scbptr(ahd, saved_scbptr);
+}
+
+/************************** SCB and SCB queue management **********************/
+int
+ahd_match_scb(struct ahd_softc *ahd, struct scb *scb, int target,
+             char channel, int lun, u_int tag, role_t role)
+{
+       int targ = SCB_GET_TARGET(ahd, scb);
+       char chan = SCB_GET_CHANNEL(ahd, scb);
+       int slun = SCB_GET_LUN(scb);
+       int match;
+
+       match = ((chan == channel) || (channel == ALL_CHANNELS));
+       if (match != 0)
+               match = ((targ == target) || (target == CAM_TARGET_WILDCARD));
+       if (match != 0)
+               match = ((lun == slun) || (lun == CAM_LUN_WILDCARD));
+       if (match != 0) {
+#if AHD_TARGET_MODE
+               int group;
+
+               group = XPT_FC_GROUP(scb->io_ctx->ccb_h.func_code);
+               if (role == ROLE_INITIATOR) {
+                       match = (group != XPT_FC_GROUP_TMODE)
+                             && ((tag == SCB_GET_TAG(scb))
+                              || (tag == SCB_LIST_NULL));
+               } else if (role == ROLE_TARGET) {
+                       match = (group == XPT_FC_GROUP_TMODE)
+                             && ((tag == scb->io_ctx->csio.tag_id)
+                              || (tag == SCB_LIST_NULL));
+               }
+#else /* !AHD_TARGET_MODE */
+               match = ((tag == SCB_GET_TAG(scb)) || (tag == SCB_LIST_NULL));
+#endif /* AHD_TARGET_MODE */
+       }
+
+       return match;
+}
+
+void
+ahd_freeze_devq(struct ahd_softc *ahd, struct scb *scb)
+{
+       int     target;
+       char    channel;
+       int     lun;
+
+       target = SCB_GET_TARGET(ahd, scb);
+       lun = SCB_GET_LUN(scb);
+       channel = SCB_GET_CHANNEL(ahd, scb);
+       
+       ahd_search_qinfifo(ahd, target, channel, lun,
+                          /*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
+                          CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+       ahd_platform_freeze_devq(ahd, scb);
+}
+
+void
+ahd_qinfifo_requeue_tail(struct ahd_softc *ahd, struct scb *scb)
+{
+       struct scb *prev_scb;
+
+       prev_scb = NULL;
+       if (ahd_qinfifo_count(ahd) != 0) {
+               u_int prev_tag;
+               u_int prev_pos;
+
+               prev_pos = AHD_QIN_WRAP(ahd->qinfifonext - 1);
+               prev_tag = ahd->qinfifo[prev_pos];
+               prev_scb = ahd_lookup_scb(ahd, prev_tag);
+       }
+       ahd_qinfifo_requeue(ahd, prev_scb, scb);
+       ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+}
+
+static void
+ahd_qinfifo_requeue(struct ahd_softc *ahd, struct scb *prev_scb,
+                   struct scb *scb)
+{
+       if (prev_scb == NULL) {
+               uint32_t busaddr;
+
+               busaddr = ahd_le32toh(scb->hscb->hscb_busaddr);
+               ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 0, busaddr & 0xFF);
+               ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 1, (busaddr >> 8) & 0xFF);
+               ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 2, (busaddr >> 16) & 0xFF);
+               ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 3, (busaddr >> 24) & 0xFF);
+       } else {
+               prev_scb->hscb->next_hscb_busaddr = scb->hscb->hscb_busaddr;
+               ahd_sync_scb(ahd, prev_scb, 
+                            BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+       }
+       ahd->qinfifo[AHD_QIN_WRAP(ahd->qinfifonext)] = SCB_GET_TAG(scb);
+       ahd->qinfifonext++;
+       scb->hscb->next_hscb_busaddr = ahd->next_queued_scb->hscb->hscb_busaddr;
+       ahd_sync_scb(ahd, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+}
+
+static int
+ahd_qinfifo_count(struct ahd_softc *ahd)
+{
+       u_int qinpos;
+       u_int wrap_qinpos;
+       u_int wrap_qinfifonext;
+
+       AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+       qinpos = ahd_get_snscb_qoff(ahd);
+       wrap_qinpos = AHD_QIN_WRAP(qinpos);
+       wrap_qinfifonext = AHD_QIN_WRAP(ahd->qinfifonext);
+       if (wrap_qinfifonext > wrap_qinpos)
+               return (wrap_qinfifonext - wrap_qinpos);
+       else
+               return (wrap_qinfifonext
+                     + NUM_ELEMENTS(ahd->qinfifo) - wrap_qinpos);
+}
+
+int
+ahd_search_qinfifo(struct ahd_softc *ahd, int target, char channel,
+                  int lun, u_int tag, role_t role, uint32_t status,
+                  ahd_search_action action)
+{
+       struct scb      *scb;
+       struct scb      *prev_scb;
+       ahd_mode_state   saved_modes;
+       u_int            qinstart;
+       u_int            qinpos;
+       u_int            qintail;
+       u_int            tid_next;
+       u_int            tid_prev;
+       u_int            scbid;
+       u_int            savedscbptr;
+       uint32_t         busaddr;
+       int              found;
+       int              targets;
+
+       /* Must be in CCHAN mode */
+       saved_modes = ahd_save_modes(ahd);
+       ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);
+
+       /*
+        * Halt any pending SCB DMA.  The sequencer will reinitiate
+        * this dma if the qinfifo is not empty once we unpause.
+        */
+       if ((ahd_inb(ahd, CCSCBCTL) & (CCARREN|CCSCBEN|CCSCBDIR))
+        == (CCARREN|CCSCBEN|CCSCBDIR)) {
+               ahd_outb(ahd, CCSCBCTL,
+                        ahd_inb(ahd, CCSCBCTL) & ~(CCARREN|CCSCBEN));
+               while ((ahd_inb(ahd, CCSCBCTL) & (CCARREN|CCSCBEN)) != 0)
+                       ;
+       }
+       /* Determine sequencer's position in the qinfifo. */
+       qintail = AHD_QIN_WRAP(ahd->qinfifonext);
+       qinstart = ahd_get_snscb_qoff(ahd);
+       qinpos = AHD_QIN_WRAP(qinstart);
+       found = 0;
+       prev_scb = NULL;
+
+       if (action == SEARCH_COMPLETE) {
+               /*
+                * Don't attempt to run any queued untagged transactions
+                * until we are done with the abort process.
+                */
+               ahd_freeze_untagged_queues(ahd);
+       }
+
+       if (action == SEARCH_PRINT) {
+               printf("qinstart = %d qinfifonext = %d\nQINFIFO:",
+                      qinstart, ahd->qinfifonext);
+       }
+
+       /*
+        * Start with an empty queue.  Entries that are not chosen
+        * for removal will be re-added to the queue as we go.
+        */
+       ahd->qinfifonext = qinstart;
+       busaddr = ahd_le32toh(ahd->next_queued_scb->hscb->hscb_busaddr);
+       ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 0, busaddr & 0xFF);
+       ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 1, (busaddr >> 8) & 0xFF);
+       ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 2, (busaddr >> 16) & 0xFF);
+       ahd_outb(ahd, NEXT_QUEUED_SCB_ADDR + 3, (busaddr >> 24) & 0xFF);
+
+       while (qinpos != qintail) {
+               scb = ahd_lookup_scb(ahd, ahd->qinfifo[qinpos]);
+               if (scb == NULL) {
+                       printf("qinpos = %d, SCB index = %d\n",
+                               qinpos, ahd->qinfifo[qinpos]);
+                       panic("Loop 1\n");
+               }
+
+               if (ahd_match_scb(ahd, scb, target, channel, lun, tag, role)) {
+                       /*
+                        * We found an scb that needs to be acted on.
+                        */
+                       found++;
+                       switch (action) {
+                       case SEARCH_COMPLETE:
+                       {
+                               cam_status ostat;
+                               cam_status cstat;
+
+                               ostat = ahd_get_transaction_status(scb);
+                               if (ostat == CAM_REQ_INPROG)
+                                       ahd_set_transaction_status(scb,
+                                                                  status);
+                               cstat = ahd_get_transaction_status(scb);
+                               if (cstat != CAM_REQ_CMP)
+                                       ahd_freeze_scb(scb);
+                               if ((scb->flags & SCB_ACTIVE) == 0)
+                                       printf("Inactive SCB in qinfifo\n");
+                               ahd_done(ahd, scb);
+
+                               /* FALLTHROUGH */
+                       }
+                       case SEARCH_REMOVE:
+                               break;
+                       case SEARCH_PRINT:
+                               printf(" 0x%x", ahd->qinfifo[qinpos]);
+                               /* FALLTHROUGH */
+                       case SEARCH_COUNT:
+                               ahd_qinfifo_requeue(ahd, prev_scb, scb);
+                               prev_scb = scb;
+                               break;
+                       }
+               } else {
+                       ahd_qinfifo_requeue(ahd, prev_scb, scb);
+                       prev_scb = scb;
+               }
+               qinpos = AHD_QIN_WRAP(qinpos+1);
+       }
+
+       ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+
+       if (action == SEARCH_PRINT)
+               printf("\nWAITING_TID_QUEUES:\n");
+
+       /*
+        * Search waiting for selection lists.  We traverse the
+        * list of "their ids" waiting for selection and, if
+        * appropriate, traverse the SCBs of each "their id"
+        * looking for matches.
+        */
+       savedscbptr = ahd_get_scbptr(ahd);
+       tid_next = ahd_inw(ahd, WAITING_TID_HEAD);
+       tid_prev = SCB_LIST_NULL;
+       targets = 0;
+       for (scbid = tid_next; !SCBID_IS_NULL(scbid); scbid = tid_next) {
+               u_int tid_head;
+
+               /*
+                * We limit based on the number of SCBs since
+                * MK_MESSAGE SCBs are not in the per-tid lists.
+                */
+               targets++;
+               if (targets > AHD_SCB_MAX) {
+                       panic("TID LIST LOOP");
+               }
+               if (scbid >= ahd->scb_data.numscbs) {
+                       printf("%s: Waiting TID List inconsistency. "
+                              "SCB index == 0x%x, yet numscbs == 0x%x.",
+                              ahd_name(ahd), scbid, ahd->scb_data.numscbs);
+                       ahd_dump_card_state(ahd);
+                       panic("for safety");
+               }
+               scb = ahd_lookup_scb(ahd, scbid);
+               if (scb == NULL) {
+                       printf("%s: SCB = 0x%x Not Active!\n",
+                              ahd_name(ahd), scbid);
+                       panic("Waiting TID List traversal\n");
+               }
+               ahd_set_scbptr(ahd, scbid);
+               tid_next = ahd_inw_scbram(ahd, SCB_NEXT2);
+               if (ahd_match_scb(ahd, scb, target, channel, CAM_LUN_WILDCARD,
+                                 SCB_LIST_NULL, ROLE_UNKNOWN) == 0) {
+                       tid_prev = scbid;
+                       continue;
+               }
+
+               /*
+                * We found a list of scbs that needs to be searched.
+                */
+               if (action == SEARCH_PRINT)
+                       printf("       %d ( ", SCB_GET_TARGET(ahd, scb));
+               tid_head = scbid;
+               found += ahd_search_scb_list(ahd, target, channel,
+                                            lun, tag, role, status,
+                                            action, &tid_head,
+                                            SCB_GET_TARGET(ahd, scb));
+               if (tid_head != scbid)
+                       ahd_stitch_tid_list(ahd, tid_prev, tid_head, tid_next);
+               if (!SCBID_IS_NULL(tid_head))
+                       tid_prev = tid_head;
+               if (action == SEARCH_PRINT)
+                       printf(")\n");
+       }
+       ahd_set_scbptr(ahd, savedscbptr);
+
+       if (action == SEARCH_COMPLETE)
+               ahd_release_untagged_queues(ahd);
+       ahd_restore_modes(ahd, saved_modes);
+       return (found);
+}
+
+static int
+ahd_search_scb_list(struct ahd_softc *ahd, int target, char channel,
+                   int lun, u_int tag, role_t role, uint32_t status,
+                   ahd_search_action action, u_int *list_head, u_int tid)
+{
+       struct  scb *scb;
+       u_int   scbid;
+       u_int   next;
+       u_int   prev;
+       int     found;
+
+       AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+       found = 0;
+       prev = SCB_LIST_NULL;
+       next = *list_head;
+       for (scbid = next; !SCBID_IS_NULL(scbid); scbid = next) {
+               if (scbid >= ahd->scb_data.numscbs) {
+                       printf("%s:SCB List inconsistency. "
+                              "SCB == 0x%x, yet numscbs == 0x%x.",
+                              ahd_name(ahd), scbid, ahd->scb_data.numscbs);
+                       ahd_dump_card_state(ahd);
+                       panic("for safety");
+               }
+               scb = ahd_lookup_scb(ahd, scbid);
+               if (scb == NULL) {
+                       printf("%s: SCB = %d Not Active!\n",
+                              ahd_name(ahd), scbid);
+                       panic("Waiting List traversal\n");
+               }
+               ahd_set_scbptr(ahd, scbid);
+               next = ahd_inw_scbram(ahd, SCB_NEXT);
+               if (ahd_match_scb(ahd, scb, target, channel,
+                                 lun, SCB_LIST_NULL, role) == 0) {
+                       prev = scbid;
+                       continue;
+               }
+               found++;
+               switch (action) {
+               case SEARCH_COMPLETE:
+               {
+                       cam_status ostat;
+                       cam_status cstat;
+
+                       ostat = ahd_get_transaction_status(scb);
+                       if (ostat == CAM_REQ_INPROG)
+                               ahd_set_transaction_status(scb, status);
+                       cstat = ahd_get_transaction_status(scb);
+                       if (cstat != CAM_REQ_CMP)
+                               ahd_freeze_scb(scb);
+                       if ((scb->flags & SCB_ACTIVE) == 0)
+                               printf("Inactive SCB in Waiting List\n");
+                       ahd_done(ahd, scb);
+                       /* FALLTHROUGH */
+               }
+               case SEARCH_REMOVE:
+                       ahd_rem_wscb(ahd, scbid, prev, next, tid);
+                       if (prev == SCB_LIST_NULL)
+                               *list_head = next;
+                       break;
+               case SEARCH_PRINT:
+                       printf("0x%x ", scbid);
+               case SEARCH_COUNT:
+                       prev = scbid;
+                       break;
+               }
+               if (found > AHD_SCB_MAX)
+                       panic("SCB LIST LOOP");
+       }
+       return (found);
+}
+
+static void
+ahd_stitch_tid_list(struct ahd_softc *ahd, u_int tid_prev,
+                   u_int tid_cur, u_int tid_next)
+{
+       AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+
+       if (SCBID_IS_NULL(tid_cur)) {
+
+               /* Bypass current TID list */
+               if (SCBID_IS_NULL(tid_prev)) {
+                       ahd_outw(ahd, WAITING_TID_HEAD, tid_next);
+               } else {
+                       ahd_set_scbptr(ahd, tid_prev);
+                       ahd_outw(ahd, SCB_NEXT2, tid_next);
+               }
+               if (SCBID_IS_NULL(tid_next))
+                       ahd_outw(ahd, WAITING_TID_TAIL, tid_prev);
+       } else {
+
+               /* Stitch through tid_cur */
+               if (SCBID_IS_NULL(tid_prev)) {
+                       ahd_outw(ahd, WAITING_TID_HEAD, tid_cur);
+               } else {
+                       ahd_set_scbptr(ahd, tid_prev);
+                       ahd_outw(ahd, SCB_NEXT2, tid_cur);
+               }
+               ahd_set_scbptr(ahd, tid_cur);
+               ahd_outw(ahd, SCB_NEXT2, tid_next);
+
+               if (SCBID_IS_NULL(tid_next))
+                       ahd_outw(ahd, WAITING_TID_TAIL, tid_cur);
+       }
+}
+
+/*
+ * Manipulate the waiting for selection list and return the
+ * scb that follows the one that we remove.
+ */
+static u_int
+ahd_rem_wscb(struct ahd_softc *ahd, u_int scbid,
+            u_int prev, u_int next, u_int tid)
+{
+       u_int tail_offset;
+
+       AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+       if (!SCBID_IS_NULL(prev)) {
+               ahd_set_scbptr(ahd, prev);
+               ahd_outw(ahd, SCB_NEXT, next);
+       }
+
+       /*
+        * SCBs that had MK_MESSAGE set in them will not
+        * be queued to the per-target lists, so don't
+        * blindly clear the tail pointer.
+        */
+       tail_offset = WAITING_SCB_TAILS + (2 * tid);
+       if (SCBID_IS_NULL(next)
+        && ahd_inw(ahd, tail_offset) == scbid)
+               ahd_outw(ahd, tail_offset, prev);
+       ahd_add_scb_to_free_list(ahd, scbid);
+       return (next);
+}
+
+/*
+ * Add the SCB as selected by SCBPTR onto the on chip list of
+ * free hardware SCBs.  This list is empty/unused if we are not
+ * performing SCB paging.
+ */
+static void
+ahd_add_scb_to_free_list(struct ahd_softc *ahd, u_int scbid)
+{
+/* XXX Need some other mechanism to designate "free". */
+       /*
+        * Invalidate the tag so that our abort
+        * routines don't think it's active.
+       ahd_outb(ahd, SCB_TAG, SCB_LIST_NULL);
+        */
+}
+
+/******************************** Error Handling ******************************/
+/*
+ * Abort all SCBs that match the given description (target/channel/lun/tag),
+ * setting their status to the passed in status if the status has not already
+ * been modified from CAM_REQ_INPROG.  This routine assumes that the sequencer
+ * is paused before it is called.
+ */
+int
+ahd_abort_scbs(struct ahd_softc *ahd, int target, char channel,
+              int lun, u_int tag, role_t role, uint32_t status)
+{
+       struct  scb *scbp;
+       struct  scb *scbp_next;
+       u_int   active_scb;
+       u_int   i, j;
+       u_int   maxtarget;
+       u_int   minlun;
+       u_int   maxlun;
+
+       int     found;
+
+       /*
+        * Don't attempt to run any queued untagged transactions
+        * until we are done with the abort process.
+        */
+       ahd_freeze_untagged_queues(ahd);
+
+       /* restore this when we're done */
+       active_scb = ahd_get_scbptr(ahd);
+
+       found = ahd_search_qinfifo(ahd, target, channel, lun, SCB_LIST_NULL,
+                                  role, CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+       /*
+        * Clean out the busy target table for any untagged commands.
+        */
+       i = 0;
+       maxtarget = 16;
+       if (target != CAM_TARGET_WILDCARD) {
+               i = target;
+               if (channel == 'B')
+                       i += 8;
+               maxtarget = i + 1;
+       }
+
+       if (lun == CAM_LUN_WILDCARD) {
+               minlun = 0;
+               maxlun = AHD_NUM_LUNS_NONPKT;
+       } else if (lun >= AHD_NUM_LUNS_NONPKT) {
+               minlun = maxlun = 0;
+       } else {
+               minlun = lun;
+               maxlun = lun + 1;
+       }
+
+       if (role != ROLE_TARGET) {
+               for (;i < maxtarget; i++) {
+                       for (j = minlun;j < maxlun; j++) {
+                               u_int scbid;
+                               u_int tcl;
+
+                               tcl = BUILD_TCL(i << 4, j);
+                               scbid = ahd_find_busy_tcl(ahd, tcl);
+                               scbp = ahd_lookup_scb(ahd, scbid);
+                               if (scbp == NULL
+                                || ahd_match_scb(ahd, scbp, target, channel,
+                                                 lun, tag, role) == 0)
+                                       continue;
+                               ahd_unbusy_tcl(ahd, BUILD_TCL_RAW(i, 'A', j));
+                       }
+               }
+       }
+
+       /*
+        * Go through the pending CCB list and look for
+        * commands for this target that are still active.
+        * These are other tagged commands that were
+        * disconnected when the reset occurred.
+        */
+       scbp_next = LIST_FIRST(&ahd->pending_scbs);
+       while (scbp_next != NULL) {
+               scbp = scbp_next;
+               scbp_next = LIST_NEXT(scbp, pending_links);
+               if (ahd_match_scb(ahd, scbp, target, channel, lun, tag, role)) {
+                       cam_status ostat;
+
+                       ostat = ahd_get_transaction_status(scbp);
+                       if (ostat == CAM_REQ_INPROG)
+                               ahd_set_transaction_status(scbp, status);
+                       if (ahd_get_transaction_status(scbp) != CAM_REQ_CMP)
+                               ahd_freeze_scb(scbp);
+                       if ((scbp->flags & SCB_ACTIVE) == 0)
+                               printf("Inactive SCB on pending list\n");
+                       ahd_done(ahd, scbp);
+                       found++;
+               }
+       }
+       ahd_set_scbptr(ahd, active_scb);
+       ahd_platform_abort_scbs(ahd, target, channel, lun, tag, role, status);
+       ahd_release_untagged_queues(ahd);
+       return found;
+}
+
+static void
+ahd_reset_current_bus(struct ahd_softc *ahd)
+{
+       uint8_t scsiseq;
+
+       AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+       ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) & ~ENSCSIRST);
+       scsiseq = ahd_inb(ahd, SCSISEQ0);
+       ahd_outb(ahd, SCSISEQ0, scsiseq | SCSIRSTO);
+       ahd_delay(AHD_BUSRESET_DELAY);
+       /* Turn off the bus reset */
+       ahd_outb(ahd, SCSISEQ0, scsiseq & ~SCSIRSTO);
+       if ((ahd->bugs & AHD_SCSIRST_BUG) != 0) {
+               /*
+                * 2A Razor #474
+                * Certain chip state is not cleared for
+                * SCSI bus resets that we initiate, so
+                * we must reset the chip.
+                */
+               ahd_reset(ahd);
+               ahd_intr_enable(ahd, /*enable*/TRUE);
+               AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+       }
+
+       ahd_clear_intstat(ahd);
+
+       /* Re-enable reset interrupts */
+       ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) | ENSCSIRST);
+}
+
+int
+ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
+{
+       struct  ahd_devinfo devinfo;
+#if AHD_TARGET_MODE
+       u_int   target;
+       u_int   max_scsiid;
+#endif
+       int     found;
+
+       ahd->pending_device = NULL;
+
+       ahd_compile_devinfo(&devinfo,
+                           CAM_TARGET_WILDCARD,
+                           CAM_TARGET_WILDCARD,
+                           CAM_LUN_WILDCARD,
+                           channel, ROLE_UNKNOWN);
+       ahd_pause(ahd);
+
+       /* Make sure the sequencer is in a safe location. */
+       ahd_clear_critical_section(ahd);
+
+       /*
+        * Run our command complete fifos to ensure that we perform
+        * completion processing on any commands that 'completed'
+        * before the reset occurred.
+        */
+       ahd_run_qoutfifo(ahd);
+#if AHD_TARGET_MODE
+       if ((ahd->flags & AHD_TARGETROLE) != 0) {
+               ahd_run_tqinfifo(ahd, /*paused*/TRUE);
+       }
+#endif
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+
+       /*
+        * Reset the bus if we are initiating this reset
+        */
+       ahd_clear_msg_state(ahd);
+       ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) & ~(ENBUSFREE|ENSCSIRST));
+       if (initiate_reset)
+               ahd_reset_current_bus(ahd);
+       ahd_clear_intstat(ahd);
+
+       /*
+        * Clean up all the state information for the
+        * pending transactions on this bus.
+        */
+       found = ahd_abort_scbs(ahd, CAM_TARGET_WILDCARD, channel,
+                              CAM_LUN_WILDCARD, SCB_LIST_NULL,
+                              ROLE_UNKNOWN, CAM_SCSI_BUS_RESET);
+
+#ifdef AHD_TARGET_MODE
+       max_scsiid = (ahd->features & AHD_WIDE) ? 15 : 7;
+
+       /*
+        * Send an immediate notify ccb to all target more peripheral
+        * drivers affected by this action.
+        */
+       for (target = 0; target <= max_scsiid; target++) {
+               struct ahd_tmode_tstate* tstate;
+               u_int lun;
+
+               tstate = ahd->enabled_targets[target];
+               if (tstate == NULL)
+                       continue;
+               for (lun = 0; lun < AHD_NUM_LUNS; lun++) {
+                       struct ahd_tmode_lstate* lstate;
+
+                       lstate = tstate->enabled_luns[lun];
+                       if (lstate == NULL)
+                               continue;
+
+                       ahd_queue_lstate_event(ahd, lstate, CAM_TARGET_WILDCARD,
+                                              EVENT_TYPE_BUS_RESET, /*arg*/0);
+                       ahd_send_lstate_events(ahd, lstate);
+               }
+       }
+#endif
+       /* Notify the XPT that a bus reset occurred */
+       ahd_send_async(ahd, devinfo.channel, CAM_TARGET_WILDCARD,
+                      CAM_LUN_WILDCARD, AC_BUS_RESET, NULL);
+
+       /*
+        * Freeze the SIMQ until our poller can determine that
+        * the bus reset has really gone away.  We set the initial
+        * timer to 0 to have the check performed as soon as possible
+        * from the timer context.
+        */
+       ahd_freeze_simq(ahd);
+       ahd_timer_reset(&ahd->reset_timer, 0, ahd_reset_poll, ahd);
+       return (found);
+}
+
+
+#define AHD_RESET_POLL_US 1000
+static void
+ahd_reset_poll(void *arg)
+{
+       struct  ahd_softc *ahd;
+       u_int   scsiseq1;
+       u_int   initiator, target, max_scsiid;
+       u_long  l;
+       u_long  s;
+       
+       ahd_list_lock(&l);
+       ahd = ahd_find_softc((struct ahd_softc *)arg);
+       if (ahd == NULL) {
+               printf("ahd_reset_poll: Instance %p no longer exists\n", arg);
+               ahd_list_unlock(&l);
+               return;
+       }
+       ahd_lock(ahd, &s);
+       if (ahd_is_paused(ahd) == 0)
+               panic("Someone unpaused the sequencer!\n");
+
+       ahd_clear_intstat(ahd);
+       if ((ahd_inb(ahd, SSTAT0) & SCSIRSTI) != 0) {
+               ahd_timer_reset(&ahd->reset_timer, AHD_RESET_POLL_US,
+                               ahd_reset_poll, ahd);
+               ahd_unlock(ahd, &s);
+       }
+
+       /* Reset is now low.  Complete chip reinitialization. */
+       ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) | ENSCSIRST);
+       scsiseq1 = ahd_inb(ahd, SCSISEQ_TEMPLATE);
+       ahd_outb(ahd, SCSISEQ1, scsiseq1 & (ENSELI|ENRSELI|ENAUTOATNP));
+
+       /*
+        * Revert to async/narrow transfers until we renegotiate.
+        */
+       max_scsiid = (ahd->features & AHD_WIDE) ? 15 : 7;
+       for (target = 0; target <= max_scsiid; target++) {
+
+               if (ahd->enabled_targets[target] == NULL)
+                       continue;
+               for (initiator = 0; initiator <= max_scsiid; initiator++) {
+                       struct ahd_devinfo devinfo;
+
+                       ahd_compile_devinfo(&devinfo, target, initiator,
+                                           CAM_LUN_WILDCARD,
+                                           'A', ROLE_UNKNOWN);
+                       ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+                                     AHD_TRANS_CUR, /*paused*/TRUE);
+                       ahd_set_syncrate(ahd, &devinfo, /*period*/0,
+                                        /*offset*/0, /*ppr_options*/0,
+                                        AHD_TRANS_CUR, /*paused*/TRUE);
+               }
+       }
+
+       ahd_clear_fifo(ahd, 0);
+       ahd_clear_fifo(ahd, 1);
+       ahd_restart(ahd);
+       ahd_unlock(ahd, &s);
+       ahd_release_simq(ahd);
+       ahd_list_unlock(&l);
+}
+
+
+/****************************** Status Processing *****************************/
+void
+ahd_handle_scb_status(struct ahd_softc *ahd, struct scb *scb)
+{
+       if (scb->hscb->shared_data.istatus.scsi_status != 0) {
+               ahd_handle_scsi_status(ahd, scb);
+       } else {
+               ahd_calc_residual(ahd, scb);
+               ahd_done(ahd, scb);
+       }
+}
+
+void
+ahd_handle_scsi_status(struct ahd_softc *ahd, struct scb *scb)
+{
+       struct hardware_scb *hscb;
+       u_int  qfreeze_cnt;
+       ahd_mode_state saved_modes;
+
+       /*
+        * The sequencer freezes its select-out queue
+        * anytime a SCSI status error occurs.  We must
+        * handle the error and decrement the QFREEZE count
+        * to allow the sequencer to continue.
+        */
+       hscb = scb->hscb; 
+
+       /* Don't want to clobber the original sense code */
+       if ((scb->flags & SCB_SENSE) != 0) {
+               /*
+                * Clear the SCB_SENSE Flag and perform
+                * a normal command completion.
+                */
+               scb->flags &= ~SCB_SENSE;
+               ahd_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+               ahd_done(ahd, scb);
+               return;
+       }
+       ahd_set_transaction_status(scb, CAM_SCSI_STATUS_ERROR);
+       /* Freeze the queue until the client sees the error. */
+       ahd_pause(ahd);
+       saved_modes = ahd_save_modes(ahd);
+       ahd_clear_critical_section(ahd);
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       ahd_freeze_devq(ahd, scb);
+       ahd_freeze_scb(scb);
+       qfreeze_cnt = ahd_inw(ahd, QFREEZE_COUNT);
+       if (qfreeze_cnt == 0) {
+               printf("%s: Bad status with 0 qfreeze count!\n", ahd_name(ahd));
+       } else {
+               qfreeze_cnt--;
+               ahd_outw(ahd, QFREEZE_COUNT, qfreeze_cnt);
+       }
+       if (qfreeze_cnt == 0)
+               ahd_outb(ahd, SEQ_FLAGS2,
+                        ahd_inb(ahd, SEQ_FLAGS2) & ~SELECTOUT_QFROZEN);
+       ahd_unpause(ahd);
+       ahd_set_scsi_status(scb, hscb->shared_data.istatus.scsi_status);
+       switch (hscb->shared_data.istatus.scsi_status) {
+       case STATUS_PKT_SENSE:
+       {
+               struct scsi_status_iu_header *siu;
+
+               ahd_sync_sense(ahd, scb, BUS_DMASYNC_POSTREAD);
+               siu = (struct scsi_status_iu_header *)scb->sense_data;
+               ahd_set_scsi_status(scb, siu->status);
+#if AHD_DEBUG
+               if ((ahd_debug & AHD_SHOW_SENSE) != 0)
+                       ahd_print_path(ahd, scb);
+                       printf("SCB 0x%x Received PKT Status of 0x%x\n",
+                              SCB_GET_TAG(scb), siu->status);
+                       printf("\tflags = 0x%x, sense len = 0x%x, "
+                              "pktfail = 0x%x\n",
+                              siu->flags, scsi_4btoul(siu->sense_length),
+                              scsi_4btoul(siu->pkt_failures_length));
+#endif
+               if ((siu->flags & SIU_RSPVALID) != 0) {
+                       ahd_print_path(ahd, scb);
+                       if (scsi_4btoul(siu->pkt_failures_length) < 4) {
+                               printf("Unable to parse pkt_failures\n");
+                       } else {
+
+                               switch (SIU_PKTFAIL_CODE(siu)) {
+                               case SIU_PFC_NONE:
+                                       printf("No packet failure found\n");
+                                       break;
+                               case SIU_PFC_CIU_FIELDS_INVALID:
+                                       printf("Invalid Command IU Field\n");
+                                       break;
+                               case SIU_PFC_TMF_NOT_SUPPORTED:
+                                       printf("TMF not supportd\n");
+                                       break;
+                               case SIU_PFC_TMF_FAILED:
+                                       printf("TMF failed\n");
+                                       break;
+                               case SIU_PFC_INVALID_TYPE_CODE:
+                                       printf("Invalid L_Q Type code\n");
+                                       break;
+                               case SIU_PFC_ILLEGAL_REQUEST:
+                                       printf("Illegal request\n");
+                               default:
+                                       break;
+                               }
+                       }
+                       if (siu->status == SCSI_STATUS_OK)
+                               ahd_set_transaction_status(scb,
+                                                          CAM_REQ_CMP_ERR);
+               }
+               if ((siu->flags & SIU_SNSVALID) != 0) {
+                       scb->flags |= SCB_PKT_SENSE;
+#if AHD_DEBUG
+                       if ((ahd_debug & AHD_SHOW_SENSE) != 0)
+                               printf("Sense data available\n");
+#endif
+               }
+               ahd_done(ahd, scb);
+               break;
+       }
+       case SCSI_STATUS_CMD_TERMINATED:
+       case SCSI_STATUS_CHECK_COND:
+       {
+               struct ahd_devinfo devinfo;
+               struct ahd_dma_seg *sg;
+               struct scsi_sense *sc;
+               struct ahd_initiator_tinfo *targ_info;
+               struct ahd_tmode_tstate *tstate;
+               struct ahd_transinfo *tinfo;
+#ifdef AHD_DEBUG
+               if (ahd_debug & AHD_SHOW_SENSE) {
+                       ahd_print_path(ahd, scb);
+                       printf("SCB %d: requests Check Status\n",
+                              SCB_GET_TAG(scb));
+               }
+#endif
+
+               if (ahd_perform_autosense(scb) == 0)
+                       break;
+
+               ahd_compile_devinfo(&devinfo, SCB_GET_OUR_ID(scb),
+                                   SCB_GET_TARGET(ahd, scb),
+                                   SCB_GET_LUN(scb),
+                                   SCB_GET_CHANNEL(ahd, scb),
+                                   ROLE_INITIATOR);
+               targ_info = ahd_fetch_transinfo(ahd,
+                                               devinfo.channel,
+                                               devinfo.our_scsiid,
+                                               devinfo.target,
+                                               &tstate);
+               tinfo = &targ_info->curr;
+               sg = scb->sg_list;
+               sc = (struct scsi_sense *)hscb->shared_data.idata.cdb;
+               /*
+                * Save off the residual if there is one.
+                */
+               ahd_update_residual(ahd, scb);
+#ifdef AHD_DEBUG
+               if (ahd_debug & AHD_SHOW_SENSE) {
+                       ahd_print_path(ahd, scb);
+                       printf("Sending Sense\n");
+               }
+#endif
+               scb->sg_count = 0;
+               sg = ahd_sg_setup(ahd, scb, sg, ahd_get_sense_bufaddr(ahd, scb),
+                                 ahd_get_sense_bufsize(ahd, scb),
+                                 /*last*/TRUE);
+               sc->opcode = REQUEST_SENSE;
+               sc->byte2 = 0;
+               if (tinfo->protocol_version <= SCSI_REV_2
+                && SCB_GET_LUN(scb) < 8)
+                       sc->byte2 = SCB_GET_LUN(scb) << 5;
+               sc->unused[0] = 0;
+               sc->unused[1] = 0;
+               sc->length = ahd_get_sense_bufsize(ahd, scb);
+               sc->control = 0;
+
+               /*
+                * We can't allow the target to disconnect.
+                * This will be an untagged transaction and
+                * having the target disconnect will make this
+                * transaction indestinguishable from outstanding
+                * tagged transactions.
+                */
+               hscb->control = 0;
+
+               /*
+                * This request sense could be because the
+                * the device lost power or in some other
+                * way has lost our transfer negotiations.
+                * Renegotiate if appropriate.  Unit attention
+                * errors will be reported before any data
+                * phases occur.
+                */
+               if (ahd_get_residual(scb) == ahd_get_transfer_length(scb)) {
+                       ahd_update_neg_request(ahd, &devinfo,
+                                              tstate, targ_info,
+                                              /*force*/TRUE);
+               }
+               if (tstate->auto_negotiate & devinfo.target_mask) {
+                       hscb->control |= MK_MESSAGE;
+                       scb->flags &=
+                           ~(SCB_NEGOTIATE|SCB_ABORT|SCB_DEVICE_RESET);
+                       scb->flags |= SCB_AUTO_NEGOTIATE;
+               }
+               hscb->cdb_len = sizeof(*sc);
+               ahd_setup_data_scb(ahd, scb);
+               scb->flags |= SCB_SENSE;
+               ahd_queue_scb(ahd, scb);
+#ifdef __FreeBSD__
+               /*
+                * Ensure we have enough time to actually
+                * retrieve the sense.
+                */
+               untimeout(ahd_timeout, (caddr_t)scb,
+                         scb->io_ctx->ccb_h.timeout_ch);
+               scb->io_ctx->ccb_h.timeout_ch =
+                   timeout(ahd_timeout, (caddr_t)scb, 5 * hz);
+#endif
+               break;
+       }
+       case SCSI_STATUS_OK:
+               printf("%s: Interrupted for staus of 0???\n",
+                      ahd_name(ahd));
+               /* FALLTHROUGH */
+       default:
+               ahd_done(ahd, scb);
+               break;
+       }
+}
+
+/*
+ * Calculate the residual for a just completed SCB.
+ */
+void
+ahd_calc_residual(struct ahd_softc *ahd, struct scb *scb)
+{
+       struct hardware_scb *hscb;
+       struct initiator_status *spkt;
+       uint32_t sgptr;
+       uint32_t resid_sgptr;
+       uint32_t resid;
+
+       /*
+        * 5 cases.
+        * 1) No residual.
+        *    SG_STATUS_VALID clear in sgptr.
+        * 2) Transferless command
+        * 3) Never performed any transfers.
+        *    sgptr has SG_FULL_RESID set.
+        * 4) No residual but target did not
+        *    save data pointers after the
+        *    last transfer, so sgptr was
+        *    never updated.
+        * 5) We have a partial residual.
+        *    Use residual_sgptr to determine
+        *    where we are.
+        */
+
+       hscb = scb->hscb;
+       sgptr = ahd_le32toh(hscb->sgptr);
+       if ((sgptr & SG_STATUS_VALID) == 0)
+               /* Case 1 */
+               return;
+       sgptr &= ~SG_STATUS_VALID;
+
+       if ((sgptr & SG_LIST_NULL) != 0)
+               /* Case 2 */
+               return;
+
+       /*
+        * Residual fields are the same in both
+        * target and initiator status packets,
+        * so we can always use the initiator fields
+        * regardless of the role for this SCB.
+        */
+       spkt = &hscb->shared_data.istatus;
+       resid_sgptr = ahd_le32toh(spkt->residual_sgptr);
+       if ((sgptr & SG_FULL_RESID) != 0) {
+               /* Case 3 */
+               resid = ahd_get_transfer_length(scb);
+       } else if ((resid_sgptr & SG_LIST_NULL) != 0) {
+               /* Case 4 */
+               return;
+       } else if ((resid_sgptr & SG_OVERRUN_RESID) != 0) {
+               ahd_print_path(ahd, scb);
+               printf("data overrun detected Tag == 0x%x.\n",
+                      SCB_GET_TAG(scb));
+               ahd_freeze_devq(ahd, scb);
+               ahd_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+               ahd_freeze_scb(scb);
+               return;
+       } else if ((resid_sgptr & ~SG_PTR_MASK) != 0) {
+               panic("Bogus resid sgptr value 0x%x\n", resid_sgptr);
+               /* NOTREACHED */
+       } else {
+               struct ahd_dma_seg *sg;
+
+               /*
+                * Remainder of the SG where the transfer
+                * stopped.  
+                */
+               resid = ahd_le32toh(spkt->residual_datacnt) & AHD_SG_LEN_MASK;
+               sg = ahd_sg_bus_to_virt(ahd, scb, resid_sgptr & SG_PTR_MASK);
+
+               /* The residual sg_ptr always points to the next sg */
+               sg--;
+
+               /*
+                * Add up the contents of all residual
+                * SG segments that are after the SG where
+                * the transfer stopped.
+                */
+               while ((ahd_le32toh(sg->len) & AHD_DMA_LAST_SEG) == 0) {
+                       sg++;
+                       resid += ahd_le32toh(sg->len) & AHD_SG_LEN_MASK;
+               }
+       }
+       if ((scb->flags & SCB_SENSE) == 0)
+               ahd_set_residual(scb, resid);
+       else
+               ahd_set_sense_residual(scb, resid);
+
+#ifdef AHD_DEBUG
+       if ((ahd_debug & AHD_SHOW_MISC) != 0) {
+               ahd_print_path(ahd, scb);
+               printf("Handled Residual of %d bytes\n", resid);
+       }
+#endif
+}
+
+/******************************* Target Mode **********************************/
+#ifdef AHD_TARGET_MODE
+/*
+ * Add a target mode event to this lun's queue
+ */
+static void
+ahd_queue_lstate_event(struct ahd_softc *ahd, struct ahd_tmode_lstate *lstate,
+                      u_int initiator_id, u_int event_type, u_int event_arg)
+{
+       struct ahd_tmode_event *event;
+       int pending;
+
+       xpt_freeze_devq(lstate->path, /*count*/1);
+       if (lstate->event_w_idx >= lstate->event_r_idx)
+               pending = lstate->event_w_idx - lstate->event_r_idx;
+       else
+               pending = AHD_TMODE_EVENT_BUFFER_SIZE + 1
+                       - (lstate->event_r_idx - lstate->event_w_idx);
+
+       if (event_type == EVENT_TYPE_BUS_RESET
+        || event_type == MSG_BUS_DEV_RESET) {
+               /*
+                * Any earlier events are irrelevant, so reset our buffer.
+                * This has the effect of allowing us to deal with reset
+                * floods (an external device holding down the reset line)
+                * without losing the event that is really interesting.
+                */
+               lstate->event_r_idx = 0;
+               lstate->event_w_idx = 0;
+               xpt_release_devq(lstate->path, pending, /*runqueue*/FALSE);
+       }
+
+       if (pending == AHD_TMODE_EVENT_BUFFER_SIZE) {
+               xpt_print_path(lstate->path);
+               printf("immediate event %x:%x lost\n",
+                      lstate->event_buffer[lstate->event_r_idx].event_type,
+                      lstate->event_buffer[lstate->event_r_idx].event_arg);
+               lstate->event_r_idx++;
+               if (lstate->event_r_idx == AHD_TMODE_EVENT_BUFFER_SIZE)
+                       lstate->event_r_idx = 0;
+               xpt_release_devq(lstate->path, /*count*/1, /*runqueue*/FALSE);
+       }
+
+       event = &lstate->event_buffer[lstate->event_w_idx];
+       event->initiator_id = initiator_id;
+       event->event_type = event_type;
+       event->event_arg = event_arg;
+       lstate->event_w_idx++;
+       if (lstate->event_w_idx == AHD_TMODE_EVENT_BUFFER_SIZE)
+               lstate->event_w_idx = 0;
+}
+
+/*
+ * Send any target mode events queued up waiting
+ * for immediate notify resources.
+ */
+void
+ahd_send_lstate_events(struct ahd_softc *ahd, struct ahd_tmode_lstate *lstate)
+{
+       struct ccb_hdr *ccbh;
+       struct ccb_immed_notify *inot;
+
+       while (lstate->event_r_idx != lstate->event_w_idx
+           && (ccbh = SLIST_FIRST(&lstate->immed_notifies)) != NULL) {
+               struct ahd_tmode_event *event;
+
+               event = &lstate->event_buffer[lstate->event_r_idx];
+               SLIST_REMOVE_HEAD(&lstate->immed_notifies, sim_links.sle);
+               inot = (struct ccb_immed_notify *)ccbh;
+               switch (event->event_type) {
+               case EVENT_TYPE_BUS_RESET:
+                       ccbh->status = CAM_SCSI_BUS_RESET|CAM_DEV_QFRZN;
+                       break;
+               default:
+                       ccbh->status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN;
+                       inot->message_args[0] = event->event_type;
+                       inot->message_args[1] = event->event_arg;
+                       break;
+               }
+               inot->initiator_id = event->initiator_id;
+               inot->sense_len = 0;
+               xpt_done((union ccb *)inot);
+               lstate->event_r_idx++;
+               if (lstate->event_r_idx == AHD_TMODE_EVENT_BUFFER_SIZE)
+                       lstate->event_r_idx = 0;
+       }
+}
+#endif
+
+/******************** Sequencer Program Patching/Download *********************/
+
+#ifdef AHD_DUMP_SEQ
+void
+ahd_dumpseq(struct ahd_softc* ahd)
+{
+       int i;
+       int max_prog;
+
+       max_prog = 2048;
+
+       ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+       ahd_outb(ahd, PRGMCNT, 0);
+       ahd_outb(ahd, PRGMCNT+1, 0);
+       for (i = 0; i < max_prog; i++) {
+               uint8_t ins_bytes[4];
+
+               ahd_insb(ahd, SEQRAM, ins_bytes, 4);
+               printf("0x%08x\n", ins_bytes[0] << 24
+                                | ins_bytes[1] << 16
+                                | ins_bytes[2] << 8
+                                | ins_bytes[3]);
+       }
+}
+#endif
+
+static void
+ahd_loadseq(struct ahd_softc *ahd)
+{
+       struct  cs cs_table[num_critical_sections];
+       u_int   begin_set[num_critical_sections];
+       u_int   end_set[num_critical_sections];
+       struct  patch *cur_patch;
+       u_int   cs_count;
+       u_int   cur_cs;
+       u_int   i;
+       int     downloaded;
+       u_int   skip_addr;
+       u_int   sg_prefetch_cnt;
+       u_int   sg_prefetch_cnt_limit;
+       u_int   sg_prefetch_align;
+       u_int   sg_size;
+       uint8_t download_consts[DOWNLOAD_CONST_COUNT];
+
+       if (bootverbose)
+               printf("%s: Downloading Sequencer Program...",
+                      ahd_name(ahd));
+
+#if DOWNLOAD_CONST_COUNT != 6
+#error "Download Const Mismatch"
+#endif
+       /*
+        * Start out with 0 critical sections
+        * that apply to this firmware load.
+        */
+       cs_count = 0;
+       cur_cs = 0;
+       memset(begin_set, 0, sizeof(begin_set));
+       memset(end_set, 0, sizeof(end_set));
+
+       /*
+        * Setup downloadable constant table.
+        * 
+        * The computation for the S/G prefetch variables is
+        * a bit complicated.  We would like to always fetch
+        * in terms of cachelined sized increments.  However,
+        * if the cacheline is not an even multiple of the
+        * SG element size or is larger than our SG RAM, using
+        * just the cache size might leave us with only a portion
+        * of an SG element at the tail of a prefetch.  If the
+        * cacheline is larger than our S/G prefetch buffer less
+        * the size of an SG element, we may round down to a cacheline
+        * that doesn't contain any or all of the S/G of interest
+        * within the bounds of our S/G ram.  Provide variables to
+        * the sequencer that will allow it to handle these edge
+        * cases.
+        */
+       /* Start by aligning to the nearest cacheline. */
+       sg_prefetch_align = ahd->pci_cachesize;
+       if (sg_prefetch_align == 0)
+               sg_prefetch_cnt = 8;
+       /* Round down to the nearest power of 2. */
+       while (powerof2(sg_prefetch_align) == 0)
+               sg_prefetch_align--;
+       /*
+        * If the cacheline boundary is greater than half our prefetch RAM
+        * we risk not being able to fetch even a single complete S/G
+        * segment if we align to that boundary.
+        */
+       if (sg_prefetch_align > CCSGADDR_MAX/2)
+               sg_prefetch_align = CCSGADDR_MAX/2;
+       /* Start by fetching a single cacheline. */
+       sg_prefetch_cnt = sg_prefetch_align;
+       /*
+        * Increment the prefetch count by cachelines until
+        * at least one S/G element will fit.
+        */
+       sg_size = sizeof(struct ahd_dma_seg);
+       if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+               sg_size = sizeof(struct ahd_dma64_seg);
+       while (sg_prefetch_cnt < sg_size)
+               sg_prefetch_cnt += sg_prefetch_align;
+       /*
+        * If the cacheline is not an even multiple of
+        * the S/G size, we may only get a partial S/G when
+        * we align. Add a cacheline if this is the case.
+        */
+       if ((sg_prefetch_align % sg_size) != 0
+        && (sg_prefetch_cnt < CCSGADDR_MAX))
+               sg_prefetch_cnt += sg_prefetch_align;
+       /*
+        * Lastly, compute a value that the sequencer can use
+        * to determine if the remainder of the CCSGRAM buffer
+        * has a full S/G element in it.
+        */
+       sg_prefetch_cnt_limit = -(sg_prefetch_cnt - sg_size + 1);
+       download_consts[SG_PREFETCH_CNT] = sg_prefetch_cnt;
+       download_consts[SG_PREFETCH_CNT_LIMIT] = sg_prefetch_cnt_limit;
+       download_consts[SG_PREFETCH_ALIGN_MASK] = ~(sg_prefetch_align - 1);
+       download_consts[SG_PREFETCH_ADDR_MASK] = (sg_prefetch_align - 1);
+       download_consts[SG_SIZEOF] = sg_size;
+       download_consts[PKT_OVERRUN_BUFOFFSET] =
+               (AHD_SCB_MAX * sizeof(uint16_t)) / 256;
+       if ((ahd->features & AHD_TARGETMODE) != 0)
+               download_consts[PKT_OVERRUN_BUFOFFSET] +=
+                   (AHD_TMODE_CMDS * sizeof(struct target_cmd)) / 256;
+
+       cur_patch = patches;
+       downloaded = 0;
+       skip_addr = 0;
+       ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+       ahd_outb(ahd, PRGMCNT, 0);
+       ahd_outb(ahd, PRGMCNT+1, 0);
+
+       for (i = 0; i < sizeof(seqprog)/4; i++) {
+               if (ahd_check_patch(ahd, &cur_patch, i, &skip_addr) == 0) {
+                       /*
+                        * Don't download this instruction as it
+                        * is in a patch that was removed.
+                        */
+                       continue;
+               }
+               /*
+                * Move through the CS table until we find a CS
+                * that might apply to this instruction.
+                */
+               for (; cur_cs < num_critical_sections; cur_cs++) {
+                       if (critical_sections[cur_cs].end <= i) {
+                               if (begin_set[cs_count] == TRUE
+                                && end_set[cs_count] == FALSE) {
+                                       cs_table[cs_count].end = downloaded;
+                                       end_set[cs_count] = TRUE;
+                                       cs_count++;
+                               }
+                               continue;
+                       }
+                       if (critical_sections[cur_cs].begin <= i
+                        && begin_set[cs_count] == FALSE) {
+                               cs_table[cs_count].begin = downloaded;
+                               begin_set[cs_count] = TRUE;
+                       }
+                       break;
+               }
+               ahd_download_instr(ahd, i, download_consts);
+               downloaded++;
+       }
+
+       ahd->num_critical_sections = cs_count;
+       if (cs_count != 0) {
+
+               cs_count *= sizeof(struct cs);
+               ahd->critical_sections = malloc(cs_count, M_DEVBUF, M_NOWAIT);
+               if (ahd->critical_sections == NULL)
+                       panic("ahd_loadseq: Could not malloc");
+               memcpy(ahd->critical_sections, cs_table, cs_count);
+       }
+       ahd_outb(ahd, SEQCTL0, PERRORDIS|FAILDIS|FASTMODE);
+
+       if (bootverbose)
+               printf(" %d instructions downloaded\n", downloaded);
+}
+
+static int
+ahd_check_patch(struct ahd_softc *ahd, struct patch **start_patch,
+               u_int start_instr, u_int *skip_addr)
+{
+       struct  patch *cur_patch;
+       struct  patch *last_patch;
+       u_int   num_patches;
+
+       num_patches = sizeof(patches)/sizeof(struct patch);
+       last_patch = &patches[num_patches];
+       cur_patch = *start_patch;
+
+       while (cur_patch < last_patch && start_instr == cur_patch->begin) {
+
+               if (cur_patch->patch_func(ahd) == 0) {
+
+                       /* Start rejecting code */
+                       *skip_addr = start_instr + cur_patch->skip_instr;
+                       cur_patch += cur_patch->skip_patch;
+               } else {
+                       /* Accepted this patch.  Advance to the next
+                        * one and wait for our intruction pointer to
+                        * hit this point.
+                        */
+                       cur_patch++;
+               }
+       }
+
+       *start_patch = cur_patch;
+       if (start_instr < *skip_addr)
+               /* Still skipping */
+               return (0);
+
+       return (1);
+}
+
+static u_int
+ahd_resolve_seqaddr(struct ahd_softc *ahd, u_int address)
+{
+       struct patch *cur_patch;
+       int address_offset;
+       u_int skip_addr;
+       u_int i;
+
+       address_offset = 0;
+       cur_patch = patches;
+       skip_addr = 0;
+
+       for (i = 0; i < address;) {
+
+               ahd_check_patch(ahd, &cur_patch, i, &skip_addr);
+
+               if (skip_addr > i) {
+                       int end_addr;
+
+                       end_addr = MIN(address, skip_addr);
+                       address_offset += end_addr - i;
+                       i = skip_addr;
+               } else {
+                       i++;
+               }
+       }
+       return (address - address_offset);
+}
+
+static void
+ahd_download_instr(struct ahd_softc *ahd, u_int instrptr, uint8_t *dconsts)
+{
+       union   ins_formats instr;
+       struct  ins_format1 *fmt1_ins;
+       struct  ins_format3 *fmt3_ins;
+       u_int   opcode;
+
+       /*
+        * The firmware is always compiled into a little endian format.
+        */
+       instr.integer = ahd_le32toh(*(uint32_t*)&seqprog[instrptr * 4]);
+
+       fmt1_ins = &instr.format1;
+       fmt3_ins = NULL;
+
+       /* Pull the opcode */
+       opcode = instr.format1.opcode;
+       switch (opcode) {
+       case AIC_OP_JMP:
+       case AIC_OP_JC:
+       case AIC_OP_JNC:
+       case AIC_OP_CALL:
+       case AIC_OP_JNE:
+       case AIC_OP_JNZ:
+       case AIC_OP_JE:
+       case AIC_OP_JZ:
+       {
+               fmt3_ins = &instr.format3;
+               fmt3_ins->address = ahd_resolve_seqaddr(ahd, fmt3_ins->address);
+               /* FALLTHROUGH */
+       }
+       case AIC_OP_OR:
+       case AIC_OP_AND:
+       case AIC_OP_XOR:
+       case AIC_OP_ADD:
+       case AIC_OP_ADC:
+       case AIC_OP_BMOV:
+               if (fmt1_ins->parity != 0) {
+                       fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+               }
+               fmt1_ins->parity = 0;
+               /* FALLTHROUGH */
+       case AIC_OP_ROL:
+       {
+               int i, count;
+
+               /* Calculate odd parity for the instruction */
+               for (i = 0, count = 0; i < 31; i++) {
+                       uint32_t mask;
+
+                       mask = 0x01 << i;
+                       if ((instr.integer & mask) != 0)
+                               count++;
+               }
+               if ((count & 0x01) == 0)
+                       instr.format1.parity = 1;
+
+               /* The sequencer is a little endian cpu */
+               instr.integer = ahd_htole32(instr.integer);
+               ahd_outsb(ahd, SEQRAM, instr.bytes, 4);
+               break;
+       }
+       default:
+               panic("Unknown opcode encountered in seq program");
+               break;
+       }
+}
+
+void
+ahd_dump_all_cards_state()
+{
+       struct ahd_softc *list_ahd;
+
+       TAILQ_FOREACH(list_ahd, &ahd_tailq, links) {
+               ahd_dump_card_state(list_ahd);
+       }
+}
+
+void
+ahd_dump_card_state(struct ahd_softc *ahd)
+{
+       struct scb      *scb;
+       ahd_mode_state   saved_modes;
+       u_int            dffstat;
+       int              paused;
+       u_int            scb_index;
+       u_int            i;
+
+       if (ahd_is_paused(ahd)) {
+               paused = 1;
+       } else {
+               paused = 0;
+               ahd_pause(ahd);
+       }
+       saved_modes = ahd_save_modes(ahd);
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       printf("%s: Dumping Card State at program address 0x%x Mode 0x%x\n",
+              ahd_name(ahd), 
+              ahd_inb(ahd, CURADDR) | (ahd_inb(ahd, CURADDR+1) << 8),
+              ahd_build_mode_state(ahd, ahd->saved_src_mode,
+                                   ahd->saved_dst_mode));
+       printf("Softc pointer is %p\n", ahd);
+       printf("IOWNID == 0x%x, TOWNID == 0x%x, SCSISEQ1 == 0x%x\n",
+              ahd_inb(ahd, IOWNID), ahd_inb(ahd, TOWNID),
+              ahd_inb(ahd, SCSISEQ1));
+       printf("SCSISIGI == 0x%x\n", ahd_inb(ahd, SCSISIGI));
+       printf("QFREEZE_COUNT == %d, SEQ_FLAGS2 == 0x%x\n",
+              ahd_inw(ahd, QFREEZE_COUNT), ahd_inb(ahd, SEQ_FLAGS2));
+       if (paused)
+               printf("Card was paused\n");
+       printf("%s: LASTSCB 0x%x CURRSCB 0x%x NEXTSCB 0x%x SEQINTCTL 0x%x\n",
+              ahd_name(ahd), ahd_inw(ahd, LASTSCB), ahd_inw(ahd, CURRSCB),
+              ahd_inw(ahd, NEXTSCB), ahd_inb(ahd, SEQINTCTL));
+       printf("SCSISEQ = 0x%x\n", ahd_inb(ahd, SCSISEQ0));
+       printf("SCB count = %d\n", ahd->scb_data.numscbs);
+       printf("Kernel NEXTQSCB = %d\n", SCB_GET_TAG(ahd->next_queued_scb));
+       printf("%s: LQCTL1 = 0x%x\n",
+              ahd_name(ahd), ahd_inb(ahd, LQCTL1));
+       printf("%s: WAITING_TID_LIST == 0x%x:0x%x\n",
+              ahd_name(ahd), ahd_inw(ahd, WAITING_TID_HEAD),
+              ahd_inw(ahd, WAITING_TID_TAIL));
+       printf("%s: WAITING_SCB_TAILS: ", ahd_name(ahd));
+       for (i = 0; i < AHD_NUM_TARGETS; i++) {
+               printf("%d(0x%x) ", i,
+                      ahd_inw(ahd, WAITING_SCB_TAILS + (2 * i)));
+       }
+       printf("\n");
+       /* QINFIFO */
+       ahd_search_qinfifo(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+                          CAM_LUN_WILDCARD, SCB_LIST_NULL,
+                          ROLE_UNKNOWN, /*status*/0, SEARCH_PRINT);
+       printf("Pending list:\n");
+       i = 0;
+       LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+               if (i++ > AHD_SCB_MAX)
+                       break;
+               if (scb != LIST_FIRST(&ahd->pending_scbs))
+                       printf(", ");
+               printf("%3d", SCB_GET_TAG(scb));
+               ahd_set_scbptr(ahd, SCB_GET_TAG(scb));
+               printf("(CTRL 0x%x ID 0x%x N 0x%x N2 0x%x SG 0x%x,"
+                      " RSG 0x%x, KSG 0x%x)\n",
+                      ahd_inb(ahd, SCB_CONTROL),
+                      ahd_inb(ahd, SCB_SCSIID), ahd_inw(ahd, SCB_NEXT),
+                      ahd_inw(ahd, SCB_NEXT2), ahd_inl(ahd, SCB_SGPTR),
+                      ahd_inl(ahd, SCB_RESIDUAL_SGPTR),
+                      ahd_le32toh(scb->hscb->sgptr));
+       }
+       printf("\n");
+
+       printf("Kernel Free SCB list: ");
+       i = 0;
+       SLIST_FOREACH(scb, &ahd->scb_data.free_scbs, links.sle) {
+               if (i++ > AHD_SCB_MAX)
+                       break;
+               printf("%d ", SCB_GET_TAG(scb));
+       }
+       printf("\n");
+
+       printf("Sequencer Complete DMA-inprog list: ");
+       scb_index = ahd_inw(ahd, COMPLETE_SCB_DMAINPROG_HEAD);
+       i = 0;
+       while (!SCBID_IS_NULL(scb_index) && i++ < AHD_SCB_MAX) {
+               ahd_set_scbptr(ahd, scb_index);
+               printf("%d ", scb_index);
+               scb_index = ahd_inw(ahd, SCB_NEXT_COMPLETE);
+       }
+       printf("\n");
+
+       printf("Sequencer Complete list: ");
+       scb_index = ahd_inw(ahd, COMPLETE_SCB_HEAD);
+       i = 0;
+       while (!SCBID_IS_NULL(scb_index) && i++ < AHD_SCB_MAX) {
+               ahd_set_scbptr(ahd, scb_index);
+               printf("%d ", scb_index);
+               scb_index = ahd_inw(ahd, SCB_NEXT_COMPLETE);
+       }
+       printf("\n");
+
+       
+       printf("Sequencer DMA-Up and Complete list: ");
+       scb_index = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
+       i = 0;
+       while (!SCBID_IS_NULL(scb_index) && i++ < AHD_SCB_MAX) {
+               ahd_set_scbptr(ahd, scb_index);
+               printf("%d ", scb_index);
+               scb_index = ahd_inw(ahd, SCB_NEXT_COMPLETE);
+       }
+       printf("\n");
+       printf("%s: SIMODE1 = 0x%x\n",
+              ahd_name(ahd), ahd_inb(ahd, SIMODE1));
+       printf("%s: LQISTAT0 = 0x%x, LQISTAT1 = 0x%x, LQISTAT2 = 0x%x\n",
+              ahd_name(ahd), ahd_inb(ahd, LQISTAT0), ahd_inb(ahd, LQISTAT1),
+              ahd_inb(ahd, LQISTAT2));
+       printf("%s: LQOSTAT0 = 0x%x, LQOSTAT1 = 0x%x, LQOSTAT2 = 0x%x\n",
+              ahd_name(ahd), ahd_inb(ahd, LQOSTAT0), ahd_inb(ahd, LQOSTAT1),
+              ahd_inb(ahd, LQOSTAT2));
+       dffstat = ahd_inb(ahd, DFFSTAT);
+       for (i = 0; i < 2; i++) {
+               struct scb *fifo_scb;
+               u_int       fifo_scbptr;
+
+               ahd_set_modes(ahd, AHD_MODE_DFF0 + i, AHD_MODE_DFF0 + i);
+               fifo_scbptr = ahd_inb(ahd, SCBPTR);
+               printf("%s: FIFO%d %s, LONGJMP == 0x%x, SCB 0x%x, LJSCB 0x%x\n",
+                      ahd_name(ahd), i,
+                      (dffstat & (FIFO0FREE << i)) ? "Free" : "Active",
+                      ahd_inw(ahd, LONGJMP_ADDR), fifo_scbptr,
+                      ahd_inw(ahd, LONGJMP_SCB));
+               printf("%s: SEQIMODE == 0x%x, SEQINTSRC == 0x%x\n",
+                      ahd_name(ahd), ahd_inb(ahd, SEQIMODE),
+                      ahd_inb(ahd, SEQINTSRC));
+               printf("%s: DFCNTRL == 0x%x, DFSTATUS == 0x%x, "
+                      "SG_CACHE_SHADOW = 0x%x, SG_STATE = 0x%x\n",
+                      ahd_name(ahd), ahd_inb(ahd, DFCNTRL),
+                      ahd_inb(ahd, DFSTATUS), ahd_inb(ahd, SG_CACHE_SHADOW),
+                      ahd_inb(ahd, SG_STATE));
+               printf("SSTAT0 = 0x%x, SSTAT1 = 0x%x, SSTAT2 = 0x%x\n",
+                      ahd_inb(ahd, SSTAT0), ahd_inb(ahd, SSTAT1),
+                      ahd_inb(ahd, SSTAT2));
+               printf("DFFSXFRCTL = 0x%x, SOFFCNT = 0x%x\n",
+                      ahd_inb(ahd, DFFSXFRCTL), ahd_inb(ahd, SOFFCNT));
+               printf("MDFFSTAT = 0x%x, SHADDR = 0x%x, SHCNT = 0x%x\n",
+                      ahd_inb(ahd, MDFFSTAT), ahd_inl(ahd, SHADDR),
+                      (ahd_inb(ahd, SHCNT)
+                     | (ahd_inb(ahd, SHCNT + 1) << 8)
+                     | (ahd_inb(ahd, SHCNT + 2) << 16)));
+               printf("HADDR = 0x%x, HCNT = 0x%x\n",
+                      ahd_inl(ahd, HADDR),
+                      (ahd_inb(ahd, HCNT)
+                     | (ahd_inb(ahd, HCNT + 1) << 8)
+                     | (ahd_inb(ahd, HCNT + 2) << 16)));
+               printf("CCSGCTL = 0x%x\n", ahd_inb(ahd, CCSGCTL));
+               fifo_scb = ahd_lookup_scb(ahd, fifo_scbptr);
+               if (fifo_scb != NULL)
+                       ahd_dump_sglist(fifo_scb);
+       }
+       printf("LQIN: ");
+       for (i = 0; i < 20; i++)
+               printf("0x%x ", ahd_inb(ahd, LQIN + i));
+       printf("\n");
+       printf("%s: SSTAT3 == 0x%x\n", ahd_name(ahd), ahd_inb(ahd, SSTAT3));
+       ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+       printf("%s: LQISTATE = 0x%x, LQOSTATE = 0x%x, OPTIONMODE = 0x%x\n",
+              ahd_name(ahd), ahd_inb(ahd, LQISTATE), ahd_inb(ahd, LQOSTATE),
+              ahd_inb(ahd, OPTIONMODE));
+       printf("%s: OS_SPACE_CNT = 0x%x MAXCMDCNT = 0x%x\n",
+              ahd_name(ahd), ahd_inb(ahd, OS_SPACE_CNT),
+              ahd_inb(ahd, MAXCMDCNT));
+       ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
+       printf("%s: REG0 == 0x%x, SINDEX = 0x%x, DINDEX = 0x%x\n",
+              ahd_name(ahd), ahd_inw(ahd, REG0), ahd_inw(ahd, SINDEX),
+              ahd_inw(ahd, DINDEX));
+       printf("%s: SCBPTR == 0x%x, SCB_NEXT == 0x%x, SCB_NEXT2 == 0x%x\n",
+              ahd_name(ahd), ahd_get_scbptr(ahd), ahd_inw(ahd, SCB_NEXT),
+              ahd_inw(ahd, SCB_NEXT2));
+       printf("CDB %x %x %x %x %x %x\n",
+              ahd_inb(ahd, SCB_CDB_STORE),
+              ahd_inb(ahd, SCB_CDB_STORE+1),
+              ahd_inb(ahd, SCB_CDB_STORE+2),
+              ahd_inb(ahd, SCB_CDB_STORE+3),
+              ahd_inb(ahd, SCB_CDB_STORE+4),
+              ahd_inb(ahd, SCB_CDB_STORE+5));
+       printf("STACK:");
+       for(i = 0; i < SEQ_STACK_SIZE; i++)
+              printf(" 0x%x", ahd_inb(ahd, STACK)|(ahd_inb(ahd, STACK) << 8));
+       printf("\n");
+       ahd_platform_dump_card_state(ahd);
+       ahd_restore_modes(ahd, saved_modes);
+       if (paused == 0)
+               ahd_unpause(ahd);
+}
+
+void
+ahd_dump_scbs(struct ahd_softc *ahd)
+{
+       ahd_mode_state saved_modes;
+       int            i;
+
+       saved_modes = ahd_save_modes(ahd);
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       for (i = 0; i < AHD_SCB_MAX; i++) {
+               ahd_set_scbptr(ahd, i);
+               printf("%3d", i);
+               printf("(CTRL 0x%x ID 0x%x N 0x%x N2 0x%x SG 0x%x, RSG 0x%x)\n",
+                      ahd_inb(ahd, SCB_CONTROL),
+                      ahd_inb(ahd, SCB_SCSIID), ahd_inw(ahd, SCB_NEXT),
+                      ahd_inw(ahd, SCB_NEXT2), ahd_inl(ahd, SCB_SGPTR),
+                      ahd_inl(ahd, SCB_RESIDUAL_SGPTR));
+       }
+       printf("\n");
+       ahd_restore_modes(ahd, saved_modes);
+}
+
+/**************************** Flexport Logic **********************************/
+/*
+ * Read count 16bit words from 16bit word address start_addr from the
+ * SEEPROM attached to the controller, into buf, using the controller's
+ * SEEPROM reading state machine.
+ */
+int
+ahd_read_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+                u_int start_addr, u_int count)
+{
+       u_int cur_addr;
+       u_int end_addr;
+       int   error;
+
+       /*
+        * If we never make it through the loop even once,
+        * we were passed invalid arguments.
+        */
+       error = EINVAL;
+       AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+       end_addr = start_addr + count;
+       for (cur_addr = start_addr; cur_addr < end_addr; cur_addr++) {
+               ahd_outb(ahd, SEEADR, cur_addr);
+               ahd_outb(ahd, SEECTL, SEEOP_READ | SEESTART);
+               
+               error = ahd_wait_seeprom(ahd);
+               if (error)
+                       break;
+               *buf++ = ahd_inw(ahd, SEEDAT);
+       }
+       return (error);
+}
+
+/*
+ * Write count 16bit words from buf, into SEEPROM attache to the
+ * controller starting at 16bit word address start_addr, using the
+ * controller's SEEPROM writing state machine.
+ */
+int
+ahd_write_seeprom(struct ahd_softc *ahd, uint16_t *buf,
+                 u_int start_addr, u_int count)
+{
+       u_int cur_addr;
+       u_int end_addr;
+       int   error;
+       int   retval;
+
+       AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+       error = ENOENT;
+
+       /* Place the chip into write-enable mode */
+       ahd_outb(ahd, SEEADR, SEEOP_EWEN_ADDR);
+       ahd_outb(ahd, SEECTL, SEEOP_EWEN | SEESTART);
+       error = ahd_wait_seeprom(ahd);
+       if (error)
+               return (error);
+
+       /*
+        * Write the data.  If we don't get throught the loop at
+        * least once, the arguments were invalid.
+        */
+       retval = EINVAL;
+       end_addr = start_addr + count;
+       for (cur_addr = start_addr; cur_addr < end_addr; cur_addr++) {
+               ahd_outw(ahd, SEEDAT, *buf++);
+               ahd_outb(ahd, SEEADR, cur_addr);
+               ahd_outb(ahd, SEECTL, SEEOP_WRITE | SEESTART);
+               
+               retval = ahd_wait_seeprom(ahd);
+               if (retval)
+                       break;
+       }
+
+       /*
+        * Disable writes.
+        */
+       ahd_outb(ahd, SEEADR, SEEOP_EWDS_ADDR);
+       ahd_outb(ahd, SEECTL, SEEOP_EWDS | SEESTART);
+       error = ahd_wait_seeprom(ahd);
+       if (error)
+               return (error);
+       return (retval);
+}
+
+/*
+ * Wait ~100us for the serial eeprom to satisfy our request.
+ */
+int
+ahd_wait_seeprom(struct ahd_softc *ahd)
+{
+       int cnt;
+
+       cnt = 20;
+       while ((ahd_inb(ahd, SEESTAT) & (SEEARBACK|SEEBUSY)) != 0 && --cnt)
+               ahd_delay(5);
+
+       if (cnt == 0)
+               return (ETIMEDOUT);
+       return (0);
+}
+
+int
+ahd_verify_cksum(struct seeprom_config *sc)
+{
+       int i;
+       int maxaddr;
+       uint32_t checksum;
+       uint16_t *scarray;
+
+       maxaddr = (sizeof(*sc)/2) - 1;
+       checksum = 0;
+       scarray = (uint16_t *)sc;
+
+       for (i = 0; i < maxaddr; i++)
+               checksum = checksum + scarray[i];
+       if (checksum == 0
+        || (checksum & 0xFFFF) != sc->checksum) {
+               return (0);
+       } else {
+               return (1);
+       }
+}
+
+int
+ahd_acquire_seeprom(struct ahd_softc *ahd)
+{
+       uint8_t seetype;
+       int     error;
+
+       error = ahd_read_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL, &seetype);
+       if (error != 0
+         || ((seetype & FLX_ROMSTAT_SEECFG) == FLX_ROMSTAT_SEE_NONE))
+               return (0);
+       return (1);
+}
+
+void
+ahd_release_seeprom(struct ahd_softc *ahd)
+{
+       /* Currently a no-op */
+}
+
+int
+ahd_write_flexport(struct ahd_softc *ahd, u_int addr, u_int value)
+{
+       int error;
+
+       AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+       if (addr > 7)
+               panic("ahd_write_flexport: address out of range");
+       ahd_outb(ahd, BRDCTL, BRDEN|(addr << 3));
+       error = ahd_wait_flexport(ahd);
+       if (error != 0)
+               return (error);
+       ahd_outb(ahd, BRDDAT, value);
+       ahd_flush_device_writes(ahd);
+       ahd_outb(ahd, BRDCTL, BRDSTB|BRDEN|(addr << 3));
+       ahd_flush_device_writes(ahd);
+       ahd_outb(ahd, BRDCTL, BRDEN|(addr << 3));
+       ahd_flush_device_writes(ahd);
+       ahd_outb(ahd, BRDCTL, 0);
+       ahd_flush_device_writes(ahd);
+       return (0);
+}
+
+int
+ahd_read_flexport(struct ahd_softc *ahd, u_int addr, uint8_t *value)
+{
+       int     error;
+
+       AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+       if (addr > 7)
+               panic("ahd_read_flexport: address out of range");
+       ahd_outb(ahd, BRDCTL, BRDRW|BRDEN|(addr << 3));
+       error = ahd_wait_flexport(ahd);
+       if (error != 0)
+               return (error);
+       *value = ahd_inb(ahd, BRDDAT);
+       ahd_outb(ahd, BRDCTL, 0);
+       ahd_flush_device_writes(ahd);
+       return (0);
+}
+
+/*
+ * Wait at most 2 seconds for flexport arbitration to succeed.
+ */
+int
+ahd_wait_flexport(struct ahd_softc *ahd)
+{
+       int cnt;
+
+       AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
+       cnt = 1000000 * 2 / 5;
+       while ((ahd_inb(ahd, BRDCTL) & FLXARBACK) == 0 && --cnt)
+               ahd_delay(5);
+
+       if (cnt == 0)
+               return (ETIMEDOUT);
+       return (0);
+}
+
+/************************* Target Mode ****************************************/
+#ifdef AHD_TARGET_MODE
+cam_status
+ahd_find_tmode_devs(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb,
+                   struct ahd_tmode_tstate **tstate,
+                   struct ahd_tmode_lstate **lstate,
+                   int notfound_failure)
+{
+
+       if ((ahd->features & AHD_TARGETMODE) == 0)
+               return (CAM_REQ_INVALID);
+
+       /*
+        * Handle the 'black hole' device that sucks up
+        * requests to unattached luns on enabled targets.
+        */
+       if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD
+        && ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
+               *tstate = NULL;
+               *lstate = ahd->black_hole;
+       } else {
+               u_int max_id;
+
+               max_id = (ahd->features & AHD_WIDE) ? 15 : 7;
+               if (ccb->ccb_h.target_id > max_id)
+                       return (CAM_TID_INVALID);
+
+               if (ccb->ccb_h.target_lun >= AHD_NUM_LUNS)
+                       return (CAM_LUN_INVALID);
+
+               *tstate = ahd->enabled_targets[ccb->ccb_h.target_id];
+               *lstate = NULL;
+               if (*tstate != NULL)
+                       *lstate =
+                           (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
+       }
+
+       if (notfound_failure != 0 && *lstate == NULL)
+               return (CAM_PATH_INVALID);
+
+       return (CAM_REQ_CMP);
+}
+
+void
+ahd_handle_en_lun(struct ahd_softc *ahd, struct cam_sim *sim, union ccb *ccb)
+{
+#if NOT_YET
+       struct     ahd_tmode_tstate *tstate;
+       struct     ahd_tmode_lstate *lstate;
+       struct     ccb_en_lun *cel;
+       cam_status status;
+       u_int      target;
+       u_int      lun;
+       u_int      target_mask;
+       u_long     s;
+       char       channel;
+
+       status = ahd_find_tmode_devs(ahd, sim, ccb, &tstate, &lstate,
+                                    /*notfound_failure*/FALSE);
+
+       if (status != CAM_REQ_CMP) {
+               ccb->ccb_h.status = status;
+               return;
+       }
+
+       if ((ahd->features & AHD_MULTIROLE) != 0) {
+               u_int      our_id;
+
+               our_id = ahd->our_id;
+               if (ccb->ccb_h.target_id != our_id) {
+                       if ((ahd->features & AHD_MULTI_TID) != 0
+                        && (ahd->flags & AHD_INITIATORROLE) != 0) {
+                               /*
+                                * Only allow additional targets if
+                                * the initiator role is disabled.
+                                * The hardware cannot handle a re-select-in
+                                * on the initiator id during a re-select-out
+                                * on a different target id.
+                                */
+                               status = CAM_TID_INVALID;
+                       } else if ((ahd->flags & AHD_INITIATORROLE) != 0
+                               || ahd->enabled_luns > 0) {
+                               /*
+                                * Only allow our target id to change
+                                * if the initiator role is not configured
+                                * and there are no enabled luns which
+                                * are attached to the currently registered
+                                * scsi id.
+                                */
+                               status = CAM_TID_INVALID;
+                       }
+               }
+       }
+
+       if (status != CAM_REQ_CMP) {
+               ccb->ccb_h.status = status;
+               return;
+       }
+
+       /*
+        * We now have an id that is valid.
+        * If we aren't in target mode, switch modes.
+        */
+       if ((ahd->flags & AHD_TARGETROLE) == 0
+        && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+               u_long  s;
+
+               printf("Configuring Target Mode\n");
+               ahd_lock(ahd, &s);
+               if (LIST_FIRST(&ahd->pending_scbs) != NULL) {
+                       ccb->ccb_h.status = CAM_BUSY;
+                       ahd_unlock(ahd, &s);
+                       return;
+               }
+               ahd->flags |= AHD_TARGETROLE;
+               if ((ahd->features & AHD_MULTIROLE) == 0)
+                       ahd->flags &= ~AHD_INITIATORROLE;
+               ahd_pause(ahd);
+               ahd_loadseq(ahd);
+               ahd_unlock(ahd, &s);
+       }
+       cel = &ccb->cel;
+       target = ccb->ccb_h.target_id;
+       lun = ccb->ccb_h.target_lun;
+       channel = SIM_CHANNEL(ahd, sim);
+       target_mask = 0x01 << target;
+       if (channel == 'B')
+               target_mask <<= 8;
+
+       if (cel->enable != 0) {
+               u_int scsiseq1;
+
+               /* Are we already enabled?? */
+               if (lstate != NULL) {
+                       xpt_print_path(ccb->ccb_h.path);
+                       printf("Lun already enabled\n");
+                       ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+                       return;
+               }
+
+               if (cel->grp6_len != 0
+                || cel->grp7_len != 0) {
+                       /*
+                        * Don't (yet?) support vendor
+                        * specific commands.
+                        */
+                       ccb->ccb_h.status = CAM_REQ_INVALID;
+                       printf("Non-zero Group Codes\n");
+                       return;
+               }
+
+               /*
+                * Seems to be okay.
+                * Setup our data structures.
+                */
+               if (target != CAM_TARGET_WILDCARD && tstate == NULL) {
+                       tstate = ahd_alloc_tstate(ahd, target, channel);
+                       if (tstate == NULL) {
+                               xpt_print_path(ccb->ccb_h.path);
+                               printf("Couldn't allocate tstate\n");
+                               ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+                               return;
+                       }
+               }
+               lstate = malloc(sizeof(*lstate), M_DEVBUF, M_NOWAIT);
+               if (lstate == NULL) {
+                       xpt_print_path(ccb->ccb_h.path);
+                       printf("Couldn't allocate lstate\n");
+                       ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+                       return;
+               }
+               memset(lstate, 0, sizeof(*lstate));
+               status = xpt_create_path(&lstate->path, /*periph*/NULL,
+                                        xpt_path_path_id(ccb->ccb_h.path),
+                                        xpt_path_target_id(ccb->ccb_h.path),
+                                        xpt_path_lun_id(ccb->ccb_h.path));
+               if (status != CAM_REQ_CMP) {
+                       free(lstate, M_DEVBUF);
+                       xpt_print_path(ccb->ccb_h.path);
+                       printf("Couldn't allocate path\n");
+                       ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+                       return;
+               }
+               SLIST_INIT(&lstate->accept_tios);
+               SLIST_INIT(&lstate->immed_notifies);
+               ahd_lock(ahd, &s);
+               ahd_pause(ahd);
+               if (target != CAM_TARGET_WILDCARD) {
+                       tstate->enabled_luns[lun] = lstate;
+                       ahd->enabled_luns++;
+
+                       if ((ahd->features & AHD_MULTI_TID) != 0) {
+                               u_int targid_mask;
+
+                               targid_mask = ahd_inb(ahd, TARGID)
+                                           | (ahd_inb(ahd, TARGID + 1) << 8);
+
+                               targid_mask |= target_mask;
+                               ahd_outb(ahd, TARGID, targid_mask);
+                               ahd_outb(ahd, TARGID+1, (targid_mask >> 8));
+                               
+                               ahd_update_scsiid(ahd, targid_mask);
+                       } else {
+                               u_int our_id;
+                               char  channel;
+
+                               channel = SIM_CHANNEL(ahd, sim);
+                               our_id = SIM_SCSI_ID(ahd, sim);
+
+                               /*
+                                * This can only happen if selections
+                                * are not enabled
+                                */
+                               if (target != our_id) {
+                                       u_int sblkctl;
+                                       char  cur_channel;
+                                       int   swap;
+
+                                       sblkctl = ahd_inb(ahd, SBLKCTL);
+                                       cur_channel = (sblkctl & SELBUSB)
+                                                   ? 'B' : 'A';
+                                       if ((ahd->features & AHD_TWIN) == 0)
+                                               cur_channel = 'A';
+                                       swap = cur_channel != channel;
+                                       ahd->our_id = target;
+
+                                       if (swap)
+                                               ahd_outb(ahd, SBLKCTL,
+                                                        sblkctl ^ SELBUSB);
+
+                                       ahd_outb(ahd, SCSIID, target);
+
+                                       if (swap)
+                                               ahd_outb(ahd, SBLKCTL, sblkctl);
+                               }
+                       }
+               } else
+                       ahd->black_hole = lstate;
+               /* Allow select-in operations */
+               if (ahd->black_hole != NULL && ahd->enabled_luns > 0) {
+                       scsiseq1 = ahd_inb(ahd, SCSISEQ_TEMPLATE);
+                       scsiseq1 |= ENSELI;
+                       ahd_outb(ahd, SCSISEQ_TEMPLATE, scsiseq1);
+                       scsiseq1 = ahd_inb(ahd, SCSISEQ1);
+                       scsiseq1 |= ENSELI;
+                       ahd_outb(ahd, SCSISEQ1, scsiseq1);
+               }
+               ahd_unpause(ahd);
+               ahd_unlock(ahd, &s);
+               ccb->ccb_h.status = CAM_REQ_CMP;
+               xpt_print_path(ccb->ccb_h.path);
+               printf("Lun now enabled for target mode\n");
+       } else {
+               struct scb *scb;
+               int i, empty;
+
+               if (lstate == NULL) {
+                       ccb->ccb_h.status = CAM_LUN_INVALID;
+                       return;
+               }
+
+               ahd_lock(ahd, &s);
+               
+               ccb->ccb_h.status = CAM_REQ_CMP;
+               LIST_FOREACH(scb, &ahd->pending_scbs, pending_links) {
+                       struct ccb_hdr *ccbh;
+
+                       ccbh = &scb->io_ctx->ccb_h;
+                       if (ccbh->func_code == XPT_CONT_TARGET_IO
+                        && !xpt_path_comp(ccbh->path, ccb->ccb_h.path)){
+                               printf("CTIO pending\n");
+                               ccb->ccb_h.status = CAM_REQ_INVALID;
+                               ahd_unlock(ahd, &s);
+                               return;
+                       }
+               }
+
+               if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
+                       printf("ATIOs pending\n");
+                       ccb->ccb_h.status = CAM_REQ_INVALID;
+               }
+
+               if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
+                       printf("INOTs pending\n");
+                       ccb->ccb_h.status = CAM_REQ_INVALID;
+               }
+
+               if (ccb->ccb_h.status != CAM_REQ_CMP) {
+                       ahd_unlock(ahd, &s);
+                       return;
+               }
+
+               xpt_print_path(ccb->ccb_h.path);
+               printf("Target mode disabled\n");
+               xpt_free_path(lstate->path);
+               free(lstate, M_DEVBUF);
+
+               ahd_pause(ahd);
+               /* Can we clean up the target too? */
+               if (target != CAM_TARGET_WILDCARD) {
+                       tstate->enabled_luns[lun] = NULL;
+                       ahd->enabled_luns--;
+                       for (empty = 1, i = 0; i < 8; i++)
+                               if (tstate->enabled_luns[i] != NULL) {
+                                       empty = 0;
+                                       break;
+                               }
+
+                       if (empty) {
+                               ahd_free_tstate(ahd, target, channel,
+                                               /*force*/FALSE);
+                               if (ahd->features & AHD_MULTI_TID) {
+                                       u_int targid_mask;
+
+                                       targid_mask = ahd_inb(ahd, TARGID)
+                                                   | (ahd_inb(ahd, TARGID + 1)
+                                                      << 8);
+
+                                       targid_mask &= ~target_mask;
+                                       ahd_outb(ahd, TARGID, targid_mask);
+                                       ahd_outb(ahd, TARGID+1,
+                                                (targid_mask >> 8));
+                                       ahd_update_scsiid(ahd, targid_mask);
+                               }
+                       }
+               } else {
+
+                       ahd->black_hole = NULL;
+
+                       /*
+                        * We can't allow selections without
+                        * our black hole device.
+                        */
+                       empty = TRUE;
+               }
+               if (ahd->enabled_luns == 0) {
+                       /* Disallow select-in */
+                       u_int scsiseq1;
+
+                       scsiseq1 = ahd_inb(ahd, SCSISEQ_TEMPLATE);
+                       scsiseq1 &= ~ENSELI;
+                       ahd_outb(ahd, SCSISEQ_TEMPLATE, scsiseq1);
+                       scsiseq1 = ahd_inb(ahd, SCSISEQ1);
+                       scsiseq1 &= ~ENSELI;
+                       ahd_outb(ahd, SCSISEQ1, scsiseq1);
+
+                       if ((ahd->features & AHD_MULTIROLE) == 0) {
+                               printf("Configuring Initiator Mode\n");
+                               ahd->flags &= ~AHD_TARGETROLE;
+                               ahd->flags |= AHD_INITIATORROLE;
+                               ahd_pause(ahd);
+                               ahd_loadseq(ahd);
+                       }
+               }
+               ahd_unpause(ahd);
+               ahd_unlock(ahd, &s);
+       }
+#endif
+}
+
+static void
+ahd_update_scsiid(struct ahd_softc *ahd, u_int targid_mask)
+{
+#if NOT_YET
+       u_int scsiid_mask;
+       u_int scsiid;
+
+       if ((ahd->features & AHD_MULTI_TID) == 0)
+               panic("ahd_update_scsiid called on non-multitid unit\n");
+
+       /*
+        * Since we will rely on the the TARGID mask
+        * for selection enables, ensure that OID
+        * in SCSIID is not set to some other ID
+        * that we don't want to allow selections on.
+        */
+       if ((ahd->features & AHD_ULTRA2) != 0)
+               scsiid = ahd_inb(ahd, SCSIID_ULTRA2);
+       else
+               scsiid = ahd_inb(ahd, SCSIID);
+       scsiid_mask = 0x1 << (scsiid & OID);
+       if ((targid_mask & scsiid_mask) == 0) {
+               u_int our_id;
+
+               /* ffs counts from 1 */
+               our_id = ffs(targid_mask);
+               if (our_id == 0)
+                       our_id = ahd->our_id;
+               else
+                       our_id--;
+               scsiid &= TID;
+               scsiid |= our_id;
+       }
+       if ((ahd->features & AHD_ULTRA2) != 0)
+               ahd_outb(ahd, SCSIID_ULTRA2, scsiid);
+       else
+               ahd_outb(ahd, SCSIID, scsiid);
+#endif
+}
+
+void
+ahd_run_tqinfifo(struct ahd_softc *ahd, int paused)
+{
+       struct target_cmd *cmd;
+
+       ahd_sync_tqinfifo(ahd, BUS_DMASYNC_POSTREAD);
+       while ((cmd = &ahd->targetcmds[ahd->tqinfifonext])->cmd_valid != 0) {
+
+               /*
+                * Only advance through the queue if we
+                * have the resources to process the command.
+                */
+               if (ahd_handle_target_cmd(ahd, cmd) != 0)
+                       break;
+
+               cmd->cmd_valid = 0;
+               ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
+                               ahd->shared_data_dmamap,
+                               ahd_targetcmd_offset(ahd, ahd->tqinfifonext),
+                               sizeof(struct target_cmd),
+                               BUS_DMASYNC_PREREAD);
+               ahd->tqinfifonext++;
+
+               /*
+                * Lazily update our position in the target mode incoming
+                * command queue as seen by the sequencer.
+                */
+               if ((ahd->tqinfifonext & (HOST_TQINPOS - 1)) == 1) {
+                       u_int hs_mailbox;
+
+                       hs_mailbox = ahd_inb(ahd, HS_MAILBOX);
+                       hs_mailbox &= ~HOST_TQINPOS;
+                       hs_mailbox |= ahd->tqinfifonext & HOST_TQINPOS;
+                       ahd_outb(ahd, HS_MAILBOX, hs_mailbox);
+               }
+       }
+}
+
+static int
+ahd_handle_target_cmd(struct ahd_softc *ahd, struct target_cmd *cmd)
+{
+       struct    ahd_tmode_tstate *tstate;
+       struct    ahd_tmode_lstate *lstate;
+       struct    ccb_accept_tio *atio;
+       uint8_t *byte;
+       int       initiator;
+       int       target;
+       int       lun;
+
+       initiator = SCSIID_TARGET(ahd, cmd->scsiid);
+       target = SCSIID_OUR_ID(cmd->scsiid);
+       lun    = (cmd->identify & MSG_IDENTIFY_LUNMASK);
+
+       byte = cmd->bytes;
+       tstate = ahd->enabled_targets[target];
+       lstate = NULL;
+       if (tstate != NULL)
+               lstate = tstate->enabled_luns[lun];
+
+       /*
+        * Commands for disabled luns go to the black hole driver.
+        */
+       if (lstate == NULL)
+               lstate = ahd->black_hole;
+
+       atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
+       if (atio == NULL) {
+               ahd->flags |= AHD_TQINFIFO_BLOCKED;
+               /*
+                * Wait for more ATIOs from the peripheral driver for this lun.
+                */
+               return (1);
+       } else
+               ahd->flags &= ~AHD_TQINFIFO_BLOCKED;
+#if AHD_DEBUG
+       if ((ahd_debug & AHD_SHOW_TQIN) != 0)
+               printf("Incoming command from %d for %d:%d%s\n",
+                      initiator, target, lun,
+                      lstate == ahd->black_hole ? "(Black Holed)" : "");
+#endif
+       SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
+
+       if (lstate == ahd->black_hole) {
+               /* Fill in the wildcards */
+               atio->ccb_h.target_id = target;
+               atio->ccb_h.target_lun = lun;
+       }
+
+       /*
+        * Package it up and send it off to
+        * whomever has this lun enabled.
+        */
+       atio->sense_len = 0;
+       atio->init_id = initiator;
+       if (byte[0] != 0xFF) {
+               /* Tag was included */
+               atio->tag_action = *byte++;
+               atio->tag_id = *byte++;
+               atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
+       } else {
+               atio->ccb_h.flags = 0;
+       }
+       byte++;
+
+       /* Okay.  Now determine the cdb size based on the command code */
+       switch (*byte >> CMD_GROUP_CODE_SHIFT) {
+       case 0:
+               atio->cdb_len = 6;
+               break;
+       case 1:
+       case 2:
+               atio->cdb_len = 10;
+               break;
+       case 4:
+               atio->cdb_len = 16;
+               break;
+       case 5:
+               atio->cdb_len = 12;
+               break;
+       case 3:
+       default:
+               /* Only copy the opcode. */
+               atio->cdb_len = 1;
+               printf("Reserved or VU command code type encountered\n");
+               break;
+       }
+       
+       memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
+
+       atio->ccb_h.status |= CAM_CDB_RECVD;
+
+       if ((cmd->identify & MSG_IDENTIFY_DISCFLAG) == 0) {
+               /*
+                * We weren't allowed to disconnect.
+                * We're hanging on the bus until a
+                * continue target I/O comes in response
+                * to this accept tio.
+                */
+#if AHD_DEBUG
+               if ((ahd_debug & AHD_SHOW_TQIN) != 0)
+                       printf("Received Immediate Command %d:%d:%d - %p\n",
+                              initiator, target, lun, ahd->pending_device);
+#endif
+               ahd->pending_device = lstate;
+               ahd_freeze_ccb((union ccb *)atio);
+               atio->ccb_h.flags |= CAM_DIS_DISCONNECT;
+       }
+       xpt_done((union ccb*)atio);
+       return (0);
+}
+
+#endif
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_host.h b/xen/drivers/scsi/aic7xxx/aic79xx_host.h
new file mode 100644 (file)
index 0000000..5427eac
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Adaptec AIC79xx device driver host template for Linux.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_host.h#6 $
+ */
+
+#ifndef _AIC79XX_LINUX_HOST_H_
+#define _AIC79XX_LINUX_HOST_H_
+
+int             ahd_linux_proc_info(char *, char **, off_t, int, int, int);
+int             ahd_linux_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+int             ahd_linux_detect(Scsi_Host_Template *);
+int             ahd_linux_release(struct Scsi_Host *);
+const char     *ahd_linux_info(struct Scsi_Host *);
+int             ahd_linux_biosparam(Disk *, kdev_t, int[]);
+int             ahd_linux_bus_reset(Scsi_Cmnd *);
+int             ahd_linux_dev_reset(Scsi_Cmnd *);
+int             ahd_linux_abort(Scsi_Cmnd *);
+
+#if defined(__i386__)
+#  define AIC79XX_BIOSPARAM ahd_linux_biosparam
+#else
+#  define AIC79XX_BIOSPARAM NULL
+#endif
+
+/*
+ * Scsi_Host_Template (see hosts.h) for AIC-79xx - some fields
+ * to do with card config are filled in after the card is detected.
+ */
+#define AIC79XX        {                                               \
+       next: NULL,                                             \
+       module: NULL,                                           \
+       proc_dir: NULL,                                         \
+       proc_info: ahd_linux_proc_info,                         \
+       name: NULL,                                             \
+       detect: ahd_linux_detect,                               \
+       release: ahd_linux_release,                             \
+       info: ahd_linux_info,                                   \
+       command: NULL,                                          \
+       queuecommand: ahd_linux_queue,                          \
+       eh_strategy_handler: NULL,                              \
+       eh_abort_handler: ahd_linux_abort,                      \
+       eh_device_reset_handler: ahd_linux_dev_reset,           \
+       eh_bus_reset_handler: ahd_linux_bus_reset,              \
+       eh_host_reset_handler: NULL,                            \
+       abort: NULL,                                            \
+       reset: NULL,                                            \
+       slave_attach: NULL,                                     \
+       bios_param: AIC79XX_BIOSPARAM,                          \
+       can_queue: AHD_MAX_QUEUE,/* max simultaneous cmds     */\
+       this_id: -1,             /* scsi id of host adapter   */\
+       sg_tablesize: AHD_NSEG,  /* max scatter-gather cmds   */\
+       cmd_per_lun: 2,          /* cmds per lun              */\
+       present: 0,              /* number of 7xxx's present  */\
+       unchecked_isa_dma: 0,    /* no memory DMA restrictions*/\
+       use_clustering: ENABLE_CLUSTERING,                      \
+       use_new_eh_code: 1                                      \
+}
+
+#endif /* _AIC79XX_LINUX_HOST_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_inline.h b/xen/drivers/scsi/aic7xxx/aic79xx_inline.h
new file mode 100644 (file)
index 0000000..72750fc
--- /dev/null
@@ -0,0 +1,997 @@
+/*
+ * Inline routines shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx_inline.h#28 $
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AIC79XX_INLINE_H_
+#define _AIC79XX_INLINE_H_
+
+/******************************** Debugging ***********************************/
+static __inline char *ahd_name(struct ahd_softc *ahd);
+
+static __inline char *
+ahd_name(struct ahd_softc *ahd)
+{
+       return (ahd->name);
+}
+
+/************************ Sequencer Execution Control *************************/
+static __inline void ahd_known_modes(struct ahd_softc *ahd,
+                                    ahd_mode src, ahd_mode dst);
+static __inline ahd_mode_state ahd_build_mode_state(struct ahd_softc *ahd,
+                                                   ahd_mode src,
+                                                   ahd_mode dst);
+static __inline void ahd_extract_mode_state(struct ahd_softc *ahd,
+                                           ahd_mode_state state,
+                                           ahd_mode *src, ahd_mode *dst);
+static __inline void ahd_set_modes(struct ahd_softc *ahd, ahd_mode src,
+                                  ahd_mode dst);
+static __inline void ahd_update_modes(struct ahd_softc *ahd);
+static __inline void ahd_assert_modes(struct ahd_softc *ahd, ahd_mode srcmode,
+                                     ahd_mode dstmode, const char *file,
+                                     int line);
+static __inline ahd_mode_state ahd_save_modes(struct ahd_softc *ahd);
+static __inline void ahd_restore_modes(struct ahd_softc *ahd,
+                                      ahd_mode_state state);
+static __inline int  ahd_is_paused(struct ahd_softc *ahd);
+static __inline void ahd_pause(struct ahd_softc *ahd);
+static __inline void ahd_unpause(struct ahd_softc *ahd);
+
+static __inline void
+ahd_known_modes(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst)
+{
+       ahd->src_mode = src;
+       ahd->dst_mode = dst;
+       ahd->saved_src_mode = src;
+       ahd->saved_dst_mode = dst;
+}
+
+static __inline ahd_mode_state
+ahd_build_mode_state(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst)
+{
+       return ((src << SRC_MODE_SHIFT) | (dst << DST_MODE_SHIFT));
+}
+
+static __inline void
+ahd_extract_mode_state(struct ahd_softc *ahd, ahd_mode_state state,
+                      ahd_mode *src, ahd_mode *dst)
+{
+       *src = (state & SRC_MODE) >> SRC_MODE_SHIFT;
+       *dst = (state & DST_MODE) >> DST_MODE_SHIFT;
+}
+
+static __inline void
+ahd_set_modes(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst)
+{
+       if (ahd->src_mode == src && ahd->dst_mode == dst)
+               return;
+#ifdef AHD_DEBUG
+       if (ahd->src_mode == AHD_MODE_UNKNOWN
+        || ahd->dst_mode == AHD_MODE_UNKNOWN)
+               panic("Setting mode prior to saving it.\n");
+       if ((ahd_debug & AHD_SHOW_MODEPTR) != 0)
+               printf("Setting mode 0x%x\n",
+                      ahd_build_mode_state(ahd, src, dst));
+#endif
+       ahd_outb(ahd, MODE_PTR, ahd_build_mode_state(ahd, src, dst));
+       ahd->src_mode = src;
+       ahd->dst_mode = dst;
+}
+
+static __inline void
+ahd_update_modes(struct ahd_softc *ahd)
+{
+       ahd_mode_state mode_ptr;
+       ahd_mode src;
+       ahd_mode dst;
+
+       mode_ptr = ahd_inb(ahd, MODE_PTR);
+#ifdef AHD_DEBUG
+       if ((ahd_debug & AHD_SHOW_MODEPTR) != 0)
+               printf("Reading mode 0x%x\n", mode_ptr);
+#endif
+       ahd_extract_mode_state(ahd, mode_ptr, &src, &dst);
+       ahd_known_modes(ahd, src, dst);
+}
+
+static __inline void
+ahd_assert_modes(struct ahd_softc *ahd, ahd_mode srcmode,
+                ahd_mode dstmode, const char *file, int line)
+{
+#ifdef AHD_DEBUG
+       if ((srcmode & AHD_MK_MSK(ahd->src_mode)) == 0
+        || (dstmode & AHD_MK_MSK(ahd->dst_mode)) == 0) {
+               panic("%s:%s:%d: Mode assertion failed.\n",
+                      ahd_name(ahd), file, line);
+       }
+#endif
+}
+
+static __inline ahd_mode_state
+ahd_save_modes(struct ahd_softc *ahd)
+{
+       if (ahd->src_mode == AHD_MODE_UNKNOWN
+        || ahd->dst_mode == AHD_MODE_UNKNOWN)
+               ahd_update_modes(ahd);
+
+       return (ahd_build_mode_state(ahd, ahd->src_mode, ahd->dst_mode));
+}
+
+static __inline void
+ahd_restore_modes(struct ahd_softc *ahd, ahd_mode_state state)
+{
+       ahd_mode src;
+       ahd_mode dst;
+
+       ahd_extract_mode_state(ahd, state, &src, &dst);
+       ahd_set_modes(ahd, src, dst);
+}
+
+#define AHD_ASSERT_MODES(ahd, source, dest) \
+       ahd_assert_modes(ahd, source, dest, __FILE__, __LINE__);
+
+/*
+ * Determine whether the sequencer has halted code execution.
+ * Returns non-zero status if the sequencer is stopped.
+ */
+static __inline int
+ahd_is_paused(struct ahd_softc *ahd)
+{
+       return ((ahd_inb(ahd, HCNTRL) & PAUSE) != 0);
+}
+
+/*
+ * Request that the sequencer stop and wait, indefinitely, for it
+ * to stop.  The sequencer will only acknowledge that it is paused
+ * once it has reached an instruction boundary and PAUSEDIS is
+ * cleared in the SEQCTL register.  The sequencer may use PAUSEDIS
+ * for critical sections.
+ */
+static __inline void
+ahd_pause(struct ahd_softc *ahd)
+{
+       ahd_outb(ahd, HCNTRL, ahd->pause);
+
+       /*
+        * Since the sequencer can disable pausing in a critical section, we
+        * must loop until it actually stops.
+        */
+       while (ahd_is_paused(ahd) == 0)
+               ;
+}
+
+/*
+ * Allow the sequencer to continue program execution.
+ * We check here to ensure that no additional interrupt
+ * sources that would cause the sequencer to halt have been
+ * asserted.  If, for example, a SCSI bus reset is detected
+ * while we are fielding a different, pausing, interrupt type,
+ * we don't want to release the sequencer before going back
+ * into our interrupt handler and dealing with this new
+ * condition.
+ */
+static __inline void
+ahd_unpause(struct ahd_softc *ahd)
+{
+       /*
+        * Automatically restore our modes to those saved
+        * prior to the first change of the mode.
+        */
+       if (ahd->saved_src_mode != AHD_MODE_UNKNOWN
+        && ahd->saved_dst_mode != AHD_MODE_UNKNOWN)
+               ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
+
+       if ((ahd_inb(ahd, INTSTAT) & ~(SWTMINT | CMDCMPLT)) == 0)
+               ahd_outb(ahd, HCNTRL, ahd->unpause);
+
+       ahd_known_modes(ahd, AHD_MODE_UNKNOWN, AHD_MODE_UNKNOWN);
+}
+
+/*********************** Untagged Transaction Routines ************************/
+static __inline void   ahd_freeze_untagged_queues(struct ahd_softc *ahd);
+static __inline void   ahd_release_untagged_queues(struct ahd_softc *ahd);
+
+/*
+ * Block our completion routine from starting the next untagged
+ * transaction for this target or target lun.
+ */
+static __inline void
+ahd_freeze_untagged_queues(struct ahd_softc *ahd)
+{
+       /*
+        * Assume we have enough space in the card's SCB
+        * to obviate the need for a per target untagged
+        * transaction limit.
+        */
+#if 0
+       ahd->untagged_queue_lock++;
+#endif
+}
+
+/*
+ * Allow the next untagged transaction for this target or target lun
+ * to be executed.  We use a counting semaphore to allow the lock
+ * to be acquired recursively.  Once the count drops to zero, the
+ * transaction queues will be run.
+ */
+static __inline void
+ahd_release_untagged_queues(struct ahd_softc *ahd)
+{
+       /*
+        * Assume we have enough space in the card's SCB
+        * to obviate the need for a per target untagged
+        * transaction limit.
+        */
+#if 0
+       ahd->untagged_queue_lock--;
+       if (ahd->untagged_queue_lock == 0)
+               ahd_run_untagged_queues(ahd);
+#endif
+}
+
+/*********************** Scatter Gather List Handling *************************/
+static __inline void   *ahd_sg_setup(struct ahd_softc *ahd, struct scb *scb,
+                                     void *sgptr, bus_addr_t addr,
+                                     bus_size_t len, int last);
+static __inline void    ahd_setup_scb_common(struct ahd_softc *ahd,
+                                             struct scb *scb);
+static __inline void    ahd_setup_data_scb(struct ahd_softc *ahd,
+                                           struct scb *scb);
+static __inline void    ahd_setup_noxfer_scb(struct ahd_softc *ahd,
+                                             struct scb *scb);
+
+static __inline void *
+ahd_sg_setup(struct ahd_softc *ahd, struct scb *scb,
+            void *sgptr, bus_addr_t addr, bus_size_t len, int last)
+{
+       scb->sg_count++;
+       if (sizeof(bus_addr_t) > 4
+        && (ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+               struct ahd_dma64_seg *sg;
+
+               sg = (struct ahd_dma64_seg *)sgptr;
+               sg->addr = ahd_htole64(addr);
+               sg->len = ahd_htole32(len | (last ? AHD_DMA_LAST_SEG : 0));
+               return (sg + 1);
+       } else {
+               struct ahd_dma_seg *sg;
+
+               sg = (struct ahd_dma_seg *)sgptr;
+               sg->addr = ahd_htole64(addr);
+               sg->len = ahd_htole32(len | ((addr >> 8) & 0x7F000000)
+                                   | (last ? AHD_DMA_LAST_SEG : 0));
+               return (sg + 1);
+       }
+}
+
+static __inline void
+ahd_setup_scb_common(struct ahd_softc *ahd, struct scb *scb)
+{
+       /* XXX Handle target mode SCBs. */
+       if ((scb->flags & SCB_PACKETIZED) != 0) {
+               /* XXX what about ACA??  It is type 4, but TAG_TYPE == 0x3. */
+               scb->hscb->task_attribute_nonpkt_tag =
+                   scb->hscb->control & SCB_TAG_TYPE;
+               scb->hscb->task_management = 0;
+       } else {
+               scb->hscb->task_attribute_nonpkt_tag = SCB_GET_TAG(scb);
+       }
+
+       if (scb->hscb->cdb_len <= MAX_CDB_LEN_WITH_SENSE_ADDR
+        || (scb->hscb->cdb_len & SCB_CDB_LEN_PTR) != 0)
+               scb->hscb->shared_data.idata.cdb_plus_saddr.sense_addr =
+                   ahd_htole32(scb->sense_busaddr);
+}
+
+static __inline void
+ahd_setup_data_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+       /*
+        * Copy the first SG into the "current" data ponter area.
+        */
+       if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
+               struct ahd_dma64_seg *sg;
+
+               sg = (struct ahd_dma64_seg *)scb->sg_list;
+               scb->hscb->dataptr = sg->addr;
+               scb->hscb->datacnt = sg->len;
+       } else {
+               struct ahd_dma_seg *sg;
+
+               sg = (struct ahd_dma_seg *)scb->sg_list;
+               scb->hscb->dataptr = sg->addr;
+               if ((ahd->flags & AHD_39BIT_ADDRESSING) != 0) {
+                       uint64_t high_addr;
+
+                       high_addr = ahd_le32toh(sg->len) & 0x7F000000;
+                       scb->hscb->dataptr |= ahd_htole64(high_addr << 8);
+               }
+               scb->hscb->datacnt = sg->len;
+       }
+       /*
+        * Note where to find the SG entries in bus space.
+        * We also set the full residual flag which the 
+        * sequencer will clear as soon as a data transfer
+        * occurs.
+        */
+       scb->hscb->sgptr = ahd_htole32(scb->sg_list_busaddr|SG_FULL_RESID);
+}
+
+static __inline void
+ahd_setup_noxfer_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+       scb->hscb->sgptr = ahd_htole32(SG_LIST_NULL);
+       scb->hscb->dataptr = 0;
+       scb->hscb->datacnt = 0;
+}
+
+/************************** Memory mapping routines ***************************/
+static __inline size_t ahd_sg_size(struct ahd_softc *ahd);
+static __inline void *
+                       ahd_sg_bus_to_virt(struct ahd_softc *ahd,
+                                          struct scb *scb,
+                                          uint32_t sg_busaddr);
+static __inline uint32_t
+                       ahd_sg_virt_to_bus(struct ahd_softc *ahd,
+                                          struct scb *scb,
+                                          void *sg);
+static __inline void   ahd_sync_scb(struct ahd_softc *ahd,
+                                    struct scb *scb, int op);
+static __inline void   ahd_sync_sglist(struct ahd_softc *ahd,
+                                       struct scb *scb, int op);
+static __inline void   ahd_sync_sense(struct ahd_softc *ahd,
+                                      struct scb *scb, int op);
+static __inline uint32_t
+                       ahd_targetcmd_offset(struct ahd_softc *ahd,
+                                            u_int index);
+
+static __inline size_t
+ahd_sg_size(struct ahd_softc *ahd)
+{
+       if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0)
+               return (sizeof(struct ahd_dma64_seg));
+       return (sizeof(struct ahd_dma_seg));
+}
+
+static __inline void *
+ahd_sg_bus_to_virt(struct ahd_softc *ahd, struct scb *scb, uint32_t sg_busaddr)
+{
+       bus_addr_t sg_offset;
+
+       /* sg_list_phys points to entry 1, not 0 */
+       sg_offset = sg_busaddr - (scb->sg_list_busaddr - ahd_sg_size(ahd));
+       return ((uint8_t *)scb->sg_list + sg_offset);
+}
+
+static __inline uint32_t
+ahd_sg_virt_to_bus(struct ahd_softc *ahd, struct scb *scb, void *sg)
+{
+       bus_addr_t sg_offset;
+
+       /* sg_list_phys points to entry 1, not 0 */
+       sg_offset = ((uint8_t *)sg - (uint8_t *)scb->sg_list)
+                 - ahd_sg_size(ahd);
+
+       return (scb->sg_list_busaddr + sg_offset);
+}
+
+static __inline void
+ahd_sync_scb(struct ahd_softc *ahd, struct scb *scb, int op)
+{
+       ahd_dmamap_sync(ahd, ahd->scb_data.hscb_dmat,
+                       scb->hscb_map->dmamap,
+                       /*offset*/(uint8_t*)scb->hscb - scb->hscb_map->vaddr,
+                       /*len*/sizeof(*scb->hscb), op);
+}
+
+static __inline void
+ahd_sync_sglist(struct ahd_softc *ahd, struct scb *scb, int op)
+{
+       if (scb->sg_count == 0)
+               return;
+
+       ahd_dmamap_sync(ahd, ahd->scb_data.sg_dmat,
+                       scb->sg_map->dmamap,
+                       /*offset*/scb->sg_list_busaddr - ahd_sg_size(ahd),
+                       /*len*/ahd_sg_size(ahd) * scb->sg_count, op);
+}
+
+static __inline void
+ahd_sync_sense(struct ahd_softc *ahd, struct scb *scb, int op)
+{
+       ahd_dmamap_sync(ahd, ahd->scb_data.sense_dmat,
+                       scb->sense_map->dmamap,
+                       /*offset*/scb->sense_busaddr,
+                       /*len*/AHD_SENSE_BUFSIZE, op);
+}
+
+static __inline uint32_t
+ahd_targetcmd_offset(struct ahd_softc *ahd, u_int index)
+{
+       return (((uint8_t *)&ahd->targetcmds[index])
+              - (uint8_t *)ahd->qoutfifo);
+}
+
+/*********************** Miscelaneous Support Functions ***********************/
+static __inline void   ahd_complete_scb(struct ahd_softc *ahd,
+                                        struct scb *scb);
+static __inline void   ahd_update_residual(struct ahd_softc *ahd,
+                                           struct scb *scb);
+static __inline struct ahd_initiator_tinfo *
+                       ahd_fetch_transinfo(struct ahd_softc *ahd,
+                                           char channel, u_int our_id,
+                                           u_int remote_id,
+                                           struct ahd_tmode_tstate **tstate);
+static __inline struct scb*
+                       ahd_get_scb(struct ahd_softc *ahd);
+static __inline void   ahd_free_scb(struct ahd_softc *ahd, struct scb *scb);
+static __inline uint16_t
+                       ahd_inw(struct ahd_softc *ahd, u_int port);
+static __inline void   ahd_outw(struct ahd_softc *ahd, u_int port,
+                                u_int value);
+static __inline uint32_t
+                       ahd_inl(struct ahd_softc *ahd, u_int port);
+static __inline void   ahd_outl(struct ahd_softc *ahd, u_int port,
+                                uint32_t value);
+static __inline uint64_t
+                       ahd_inq(struct ahd_softc *ahd, u_int port);
+static __inline void   ahd_outq(struct ahd_softc *ahd, u_int port,
+                                uint64_t value);
+static __inline u_int  ahd_get_scbptr(struct ahd_softc *ahd);
+static __inline void   ahd_set_scbptr(struct ahd_softc *ahd, u_int scbptr);
+static __inline u_int  ahd_get_hnscb_qoff(struct ahd_softc *ahd);
+static __inline void   ahd_set_hnscb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int  ahd_get_hescb_qoff(struct ahd_softc *ahd);
+static __inline void   ahd_set_hescb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int  ahd_get_snscb_qoff(struct ahd_softc *ahd);
+static __inline void   ahd_set_snscb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int  ahd_get_sescb_qoff(struct ahd_softc *ahd);
+static __inline void   ahd_set_sescb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int  ahd_get_sdscb_qoff(struct ahd_softc *ahd);
+static __inline void   ahd_set_sdscb_qoff(struct ahd_softc *ahd, u_int value);
+static __inline u_int  ahd_inb_scbram(struct ahd_softc *ahd, u_int offset);
+static __inline u_int  ahd_inw_scbram(struct ahd_softc *ahd, u_int offset);
+static __inline uint32_t
+                       ahd_inl_scbram(struct ahd_softc *ahd, u_int offset);
+static __inline void   ahd_swap_with_next_hscb(struct ahd_softc *ahd,
+                                               struct scb *scb);
+static __inline void   ahd_queue_scb(struct ahd_softc *ahd, struct scb *scb);
+static __inline uint8_t *
+                       ahd_get_sense_buf(struct ahd_softc *ahd,
+                                         struct scb *scb);
+static __inline uint32_t
+                       ahd_get_sense_bufaddr(struct ahd_softc *ahd,
+                                             struct scb *scb);
+
+static __inline void
+ahd_complete_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+       uint32_t sgptr;
+
+       sgptr = ahd_le32toh(scb->hscb->sgptr);
+       if ((sgptr & SG_STATUS_VALID) != 0)
+               ahd_handle_scb_status(ahd, scb);
+       else
+               ahd_done(ahd, scb);
+}
+
+/*
+ * Determine whether the sequencer reported a residual
+ * for this SCB/transaction.
+ */
+static __inline void
+ahd_update_residual(struct ahd_softc *ahd, struct scb *scb)
+{
+       uint32_t sgptr;
+
+       sgptr = ahd_le32toh(scb->hscb->sgptr);
+       if ((sgptr & SG_STATUS_VALID) != 0)
+               ahd_calc_residual(ahd, scb);
+}
+
+/*
+ * Return pointers to the transfer negotiation information
+ * for the specified our_id/remote_id pair.
+ */
+static __inline struct ahd_initiator_tinfo *
+ahd_fetch_transinfo(struct ahd_softc *ahd, char channel, u_int our_id,
+                   u_int remote_id, struct ahd_tmode_tstate **tstate)
+{
+       /*
+        * Transfer data structures are stored from the perspective
+        * of the target role.  Since the parameters for a connection
+        * in the initiator role to a given target are the same as
+        * when the roles are reversed, we pretend we are the target.
+        */
+       if (channel == 'B')
+               our_id += 8;
+       *tstate = ahd->enabled_targets[our_id];
+       return (&(*tstate)->transinfo[remote_id]);
+}
+
+/*
+ * Get a free scb. If there are none, see if we can allocate a new SCB.
+ */
+static __inline struct scb *
+ahd_get_scb(struct ahd_softc *ahd)
+{
+       struct scb *scb;
+
+       if ((scb = SLIST_FIRST(&ahd->scb_data.free_scbs)) == NULL) {
+               ahd_alloc_scbs(ahd);
+               scb = SLIST_FIRST(&ahd->scb_data.free_scbs);
+               if (scb == NULL)
+                       return (NULL);
+       }
+       SLIST_REMOVE_HEAD(&ahd->scb_data.free_scbs, links.sle);
+       return (scb);
+}
+
+/*
+ * Return an SCB resource to the free list.
+ */
+static __inline void
+ahd_free_scb(struct ahd_softc *ahd, struct scb *scb)
+{       
+
+       /* Clean up for the next user */
+       ahd->scb_data.scbindex[SCB_GET_TAG(scb)] = NULL;
+       scb->flags = SCB_FREE;
+       scb->hscb->control = 0;
+
+       SLIST_INSERT_HEAD(&ahd->scb_data.free_scbs, scb, links.sle);
+
+       /* Notify the OSM that a resource is now available. */
+       ahd_platform_scb_free(ahd, scb);
+}
+
+static __inline uint16_t
+ahd_inw(struct ahd_softc *ahd, u_int port)
+{
+       return ((ahd_inb(ahd, port+1) << 8) | ahd_inb(ahd, port));
+}
+
+static __inline void
+ahd_outw(struct ahd_softc *ahd, u_int port, u_int value)
+{
+       ahd_outb(ahd, port, value & 0xFF);
+       ahd_outb(ahd, port+1, (value >> 8) & 0xFF);
+}
+
+static __inline uint32_t
+ahd_inl(struct ahd_softc *ahd, u_int port)
+{
+       return ((ahd_inb(ahd, port))
+             | (ahd_inb(ahd, port+1) << 8)
+             | (ahd_inb(ahd, port+2) << 16)
+             | (ahd_inb(ahd, port+3) << 24));
+}
+
+static __inline void
+ahd_outl(struct ahd_softc *ahd, u_int port, uint32_t value)
+{
+       ahd_outb(ahd, port, (value) & 0xFF);
+       ahd_outb(ahd, port+1, ((value) >> 8) & 0xFF);
+       ahd_outb(ahd, port+2, ((value) >> 16) & 0xFF);
+       ahd_outb(ahd, port+3, ((value) >> 24) & 0xFF);
+}
+
+static __inline uint64_t
+ahd_inq(struct ahd_softc *ahd, u_int port)
+{
+       return ((ahd_inb(ahd, port))
+             | (ahd_inb(ahd, port+1) << 8)
+             | (ahd_inb(ahd, port+2) << 16)
+             | (ahd_inb(ahd, port+3) << 24)
+             | (((uint64_t)ahd_inb(ahd, port+4)) << 32)
+             | (((uint64_t)ahd_inb(ahd, port+5)) << 40)
+             | (((uint64_t)ahd_inb(ahd, port+6)) << 48)
+             | (((uint64_t)ahd_inb(ahd, port+7)) << 56));
+}
+
+static __inline void
+ahd_outq(struct ahd_softc *ahd, u_int port, uint64_t value)
+{
+       ahd_outb(ahd, port, value & 0xFF);
+       ahd_outb(ahd, port+1, (value >> 8) & 0xFF);
+       ahd_outb(ahd, port+2, (value >> 16) & 0xFF);
+       ahd_outb(ahd, port+3, (value >> 24) & 0xFF);
+       ahd_outb(ahd, port+4, (value >> 32) & 0xFF);
+       ahd_outb(ahd, port+5, (value >> 40) & 0xFF);
+       ahd_outb(ahd, port+6, (value >> 48) & 0xFF);
+       ahd_outb(ahd, port+7, (value >> 56) & 0xFF);
+}
+
+static __inline u_int
+ahd_get_scbptr(struct ahd_softc *ahd)
+{
+       AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+                        ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+       return (ahd_inb(ahd, SCBPTR) | (ahd_inb(ahd, SCBPTR + 1) << 8));
+}
+
+static __inline void
+ahd_set_scbptr(struct ahd_softc *ahd, u_int scbptr)
+{
+       AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
+                        ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
+       ahd_outb(ahd, SCBPTR, scbptr & 0xFF);
+       ahd_outb(ahd, SCBPTR+1, (scbptr >> 8) & 0xFF);
+}
+
+static __inline u_int
+ahd_get_hnscb_qoff(struct ahd_softc *ahd)
+{
+       return (ahd_inw_atomic(ahd, HNSCB_QOFF));
+}
+
+static __inline void
+ahd_set_hnscb_qoff(struct ahd_softc *ahd, u_int value)
+{
+       ahd_outw_atomic(ahd, HNSCB_QOFF, value);
+}
+
+static __inline u_int
+ahd_get_hescb_qoff(struct ahd_softc *ahd)
+{
+       return (ahd_inb(ahd, HESCB_QOFF));
+}
+
+static __inline void
+ahd_set_hescb_qoff(struct ahd_softc *ahd, u_int value)
+{
+       ahd_outb(ahd, HESCB_QOFF, value);
+}
+
+static __inline u_int
+ahd_get_snscb_qoff(struct ahd_softc *ahd)
+{
+       u_int oldvalue;
+
+       AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+       oldvalue = ahd_inw(ahd, SNSCB_QOFF);
+       ahd_outw(ahd, SNSCB_QOFF, oldvalue);
+       return (oldvalue);
+}
+
+static __inline void
+ahd_set_snscb_qoff(struct ahd_softc *ahd, u_int value)
+{
+       AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+       ahd_outw(ahd, SNSCB_QOFF, value);
+}
+
+static __inline u_int
+ahd_get_sescb_qoff(struct ahd_softc *ahd)
+{
+       AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+       return (ahd_inb(ahd, SESCB_QOFF));
+}
+
+static __inline void
+ahd_set_sescb_qoff(struct ahd_softc *ahd, u_int value)
+{
+       AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+       ahd_outb(ahd, SESCB_QOFF, value);
+}
+
+static __inline u_int
+ahd_get_sdscb_qoff(struct ahd_softc *ahd)
+{
+       AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+       return (ahd_inb(ahd, SDSCB_QOFF) | (ahd_inb(ahd, SDSCB_QOFF + 1) << 8));
+}
+
+static __inline void
+ahd_set_sdscb_qoff(struct ahd_softc *ahd, u_int value)
+{
+       AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
+       ahd_outb(ahd, SDSCB_QOFF, value & 0xFF);
+       ahd_outb(ahd, SDSCB_QOFF+1, (value >> 8) & 0xFF);
+}
+
+static __inline u_int
+ahd_inb_scbram(struct ahd_softc *ahd, u_int offset)
+{
+       u_int value;
+
+       /*
+        * Workaround PCI-X Rev A. hardware bug.
+        * After a host read of SCB memory, the chip
+        * may become confused into thinking prefetch
+        * was required.  This starts the discard timer
+        * running and can cause an unexpected discard
+        * timer interrupt.  The work around is to read
+        * a normal register prior to the exhaustion of
+        * the discard timer.  The mode pointer register
+        * has no side effects and so serves well for
+        * this purpose.
+        *
+        * Razor #528
+        */
+       value = ahd_inb(ahd, offset);
+       ahd_inb(ahd, MODE_PTR);
+       return (value);
+}
+
+static __inline u_int
+ahd_inw_scbram(struct ahd_softc *ahd, u_int offset)
+{
+       return (ahd_inb_scbram(ahd, offset)
+             | (ahd_inb_scbram(ahd, offset+1) << 8));
+}
+
+static __inline uint32_t
+ahd_inl_scbram(struct ahd_softc *ahd, u_int offset)
+{
+       return (ahd_inb_scbram(ahd, offset)
+             | (ahd_inb_scbram(ahd, offset+1) << 8)
+             | (ahd_inb_scbram(ahd, offset+2) << 16)
+             | (ahd_inb_scbram(ahd, offset+3) << 24));
+}
+
+static __inline struct scb *
+ahd_lookup_scb(struct ahd_softc *ahd, u_int tag)
+{
+       struct scb* scb;
+
+       if (tag >= AHD_SCB_MAX)
+               return (NULL);
+       scb = ahd->scb_data.scbindex[tag];
+       if (scb != NULL)
+               ahd_sync_scb(ahd, scb,
+                            BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+       return (scb);
+}
+
+static __inline void
+ahd_swap_with_next_hscb(struct ahd_softc *ahd, struct scb *scb)
+{
+       struct hardware_scb *q_hscb;
+       uint32_t saved_hscb_busaddr;
+
+       /*
+        * Our queuing method is a bit tricky.  The card
+        * knows in advance which HSCB (by address) to download,
+        * and we can't disappoint it.  To achieve this, the next
+        * SCB to download is saved off in ahd->next_queued_scb.
+        * When we are called to queue "an arbitrary scb",
+        * we copy the contents of the incoming HSCB to the one
+        * the sequencer knows about, swap HSCB pointers and
+        * finally assign the SCB to the tag indexed location
+        * in the scb_array.  This makes sure that we can still
+        * locate the correct SCB by SCB_TAG.
+        */
+       q_hscb = ahd->next_queued_scb->hscb;
+       saved_hscb_busaddr = q_hscb->hscb_busaddr;
+       memcpy(q_hscb, scb->hscb, sizeof(*scb->hscb));
+       q_hscb->hscb_busaddr = saved_hscb_busaddr;
+       q_hscb->next_hscb_busaddr = scb->hscb->hscb_busaddr;
+
+       /* Now swap HSCB pointers. */
+       ahd->next_queued_scb->hscb = scb->hscb;
+       scb->hscb = q_hscb;
+
+       /* Now define the mapping from tag to SCB in the scbindex */
+/* XXX This should be constant now.  Can we avoid the mapping? */
+       ahd->scb_data.scbindex[SCB_GET_TAG(scb)] = scb;
+}
+
+/*
+ * Tell the sequencer about a new transaction to execute.
+ */
+static __inline void
+ahd_queue_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+       ahd_swap_with_next_hscb(ahd, scb);
+
+       if (SCBID_IS_NULL(SCB_GET_TAG(scb)))
+               panic("Attempt to queue invalid SCB tag %x\n",
+                     SCB_GET_TAG(scb));
+
+       /*
+        * Keep a history of SCBs we've downloaded in the qinfifo.
+        */
+       ahd->qinfifo[AHD_QIN_WRAP(ahd->qinfifonext)] = SCB_GET_TAG(scb);
+       ahd->qinfifonext++;
+
+       if (scb->sg_count != 0)
+               ahd_setup_data_scb(ahd, scb);
+       else
+               ahd_setup_noxfer_scb(ahd, scb);
+       ahd_setup_scb_common(ahd, scb);
+
+       /*
+        * Make sure our data is consistant from the
+        * perspective of the adapter.
+        */
+       ahd_sync_scb(ahd, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+#ifdef AHD_DEBUG
+       if ((ahd_debug & AHD_SHOW_QUEUE) != 0) {
+               printf("%s: Queueing SCB 0x%x bus addr 0x%x - 0x%x%x/0x%x\n",
+                      ahd_name(ahd),
+                      SCB_GET_TAG(scb), scb->hscb->hscb_busaddr,
+                      (u_int)((scb->hscb->dataptr >> 32) & 0xFFFFFFFF),
+                      (u_int)(scb->hscb->dataptr & 0xFFFFFFFF),
+                      scb->hscb->datacnt);
+       }
+#endif
+       /* Tell the adapter about the newly queued SCB */
+       ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
+}
+
+static __inline uint8_t *
+ahd_get_sense_buf(struct ahd_softc *ahd, struct scb *scb)
+{
+       return (scb->sense_data);
+}
+
+static __inline uint32_t
+ahd_get_sense_bufaddr(struct ahd_softc *ahd, struct scb *scb)
+{
+       return (scb->sense_busaddr);
+}
+
+/************************** Interrupt Processing ******************************/
+static __inline void   ahd_sync_qoutfifo(struct ahd_softc *ahd, int op);
+static __inline void   ahd_sync_tqinfifo(struct ahd_softc *ahd, int op);
+static __inline u_int  ahd_check_cmdcmpltqueues(struct ahd_softc *ahd);
+static __inline void   ahd_intr(struct ahd_softc *ahd);
+
+static __inline void
+ahd_sync_qoutfifo(struct ahd_softc *ahd, int op)
+{
+       ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_dmamap,
+                       /*offset*/0, /*len*/AHC_SCB_MAX * sizeof(uint16_t), op);
+}
+
+static __inline void
+ahd_sync_tqinfifo(struct ahd_softc *ahd, int op)
+{
+#ifdef AHD_TARGET_MODE
+       if ((ahd->flags & AHD_TARGETROLE) != 0) {
+               ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
+                               ahd->shared_data_dmamap,
+                               ahd_targetcmd_offset(ahd, 0),
+                               sizeof(struct target_cmd) * AHD_TMODE_CMDS,
+                               op);
+       }
+#endif
+}
+
+/*
+ * See if the firmware has posted any completed commands
+ * into our in-core command complete fifos.
+ */
+#define AHD_RUN_QOUTFIFO 0x1
+#define AHD_RUN_TQINFIFO 0x2
+static __inline u_int
+ahd_check_cmdcmpltqueues(struct ahd_softc *ahd)
+{
+       u_int retval;
+
+       retval = 0;
+       ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_dmamap,
+                       /*offset*/ahd->qoutfifonext, /*len*/2,
+                       BUS_DMASYNC_POSTREAD);
+       if (ahd->qoutfifo[ahd->qoutfifonext] != SCB_LIST_NULL_LE)
+               retval |= AHD_RUN_QOUTFIFO;
+#ifdef AHD_TARGET_MODE
+       if ((ahd->flags & AHD_TARGETROLE) != 0
+        && (ahd->flags & AHD_TQINFIFO_BLOCKED) == 0) {
+               ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
+                               ahd->shared_data_dmamap,
+                               ahd_targetcmd_offset(ahd, ahd->tqinfifofnext),
+                               /*len*/sizeof(struct target_cmd),
+                               BUS_DMASYNC_POSTREAD);
+               if (ahd->targetcmds[ahd->tqinfifonext].cmd_valid != 0)
+                       retval |= AHD_RUN_TQINFIFO;
+       }
+#endif
+       return (retval);
+}
+
+/*
+ * Catch an interrupt from the adapter
+ */
+static __inline void
+ahd_intr(struct ahd_softc *ahd)
+{
+       u_int   intstat;
+
+       /*
+        * Instead of directly reading the interrupt status register,
+        * infer the cause of the interrupt by checking our in-core
+        * completion queues.  This avoids a costly PCI bus read in
+        * most cases.
+        */
+       if ((ahd->flags & AHD_ALL_INTERRUPTS) == 0
+        && (ahd_check_cmdcmpltqueues(ahd) != 0))
+               intstat = CMDCMPLT;
+       else
+               intstat = ahd_inb(ahd, INTSTAT);
+
+       if (intstat & CMDCMPLT) {
+               ahd_outb(ahd, CLRINT, CLRCMDINT);
+
+               /*
+                * Ensure that the chip sees that we've cleared
+                * this interrupt before we walk the output fifo.
+                * Otherwise, we may, due to posted bus writes,
+                * clear the interrupt after we finish the scan,
+                * and after the sequencer has added new entries
+                * and asserted the interrupt again.
+                */
+               ahd_flush_device_writes(ahd);
+               ahd_run_qoutfifo(ahd);
+#ifdef AHD_TARGET_MODE
+               if ((ahd->flags & AHD_TARGETROLE) != 0)
+                       ahd_run_tqinfifo(ahd, /*paused*/FALSE);
+#endif
+       }
+
+       if (intstat == 0xFF && (ahd->features & AHD_REMOVABLE) != 0)
+               /* Hot eject */
+               return;
+
+       if ((intstat & INT_PEND) == 0)
+               return;
+
+       if (intstat & HWERRINT) {
+               ahd_handle_hwerrint(ahd);
+               return;
+       }
+
+       if ((intstat & (PCIINT|SPLTINT)) != 0) {
+               ahd->bus_intr(ahd);
+               return;
+       }
+
+       if ((intstat & SEQINT) != 0)
+               ahd_handle_seqint(ahd, intstat);
+
+       if ((intstat & SCSIINT) != 0)
+               ahd_handle_scsiint(ahd, intstat);
+}
+
+#endif  /* _AIC79XX_INLINE_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_osm.c b/xen/drivers/scsi/aic7xxx/aic79xx_osm.c
new file mode 100644 (file)
index 0000000..9d3c4c1
--- /dev/null
@@ -0,0 +1,2927 @@
+/*
+ * Adaptec AIC79xx device driver for Linux.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.c#36 $
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-2000 Justin T. Gibbs.
+ * Copyright (c) 1997-1999 Doug Ledford
+ * Copyright (c) 2000-2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+/*
+ * This is the only file where module.h should
+ * embed module global version info.
+ */
+#define AHD_MODVERSION_FILE
+
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+#include <linux/init.h>                /* __setup */
+#endif
+
+#include "../sd.h"             /* For geometry detection */
+
+#include <linux/mm.h>          /* For fetching system memory size */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+/*
+ * Lock protecting manipulation of the ahd softc list.
+ */
+spinlock_t ahd_list_spinlock;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+struct proc_dir_entry proc_scsi_aic79xx = {
+       PROC_SCSI_AIC79XX, 7, "aic79xx",
+       S_IFDIR | S_IRUGO | S_IXUGO, 2,
+       0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+#endif
+
+/*
+ * Set this to the delay in seconds after SCSI bus reset.
+ * Note, we honor this only for the initial bus reset.
+ * The scsi error recovery code performs its own bus settle
+ * delay handling for error recovery actions.
+ */
+#ifdef CONFIG_AIC79XX_RESET_DELAY_MS
+#define AIC79XX_RESET_DELAY CONFIG_AIC79XX_RESET_DELAY_MS
+#else
+#define AIC79XX_RESET_DELAY 5000
+#endif
+
+/*
+ * To change the default number of tagged transactions allowed per-device,
+ * add a line to the lilo.conf file like:
+ * append="aic79xx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}"
+ * which will result in the first four devices on the first two
+ * controllers being set to a tagged queue depth of 32.
+ *
+ * The tag_commands is an array of 16 to allow for wide and twin adapters.
+ * Twin adapters will use indexes 0-7 for channel 0, and indexes 8-15
+ * for channel 1.
+ */
+typedef struct {
+       uint16_t tag_commands[16];      /* Allow for wide/twin adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Modify this as you see fit for your system.
+ *
+ * 0                   tagged queuing disabled
+ * 1 <= n <= 253       n == max tags ever dispatched.
+ *
+ * The driver will throttle the number of commands dispatched to a
+ * device if it returns queue full.  For devices with a fixed maximum
+ * queue depth, the driver will eventually determine this depth and
+ * lock it in (a console message is printed to indicate that a lock
+ * has occurred).  On some devices, queue full is returned for a temporary
+ * resource shortage.  These devices will return queue full at varying
+ * depths.  The driver will throttle back when the queue fulls occur and
+ * attempt to slowly increase the depth over time as the device recovers
+ * from the resource shortage.
+ *
+ * In this example, the first line will disable tagged queueing for all
+ * the devices on the first probed aic79xx adapter.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (0, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to attempt to use up to 64 tags for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3.  It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+
+/*
+ * NOTE: The below structure is for reference only, the actual structure
+ *       to modify in order to change things is just below this comment block.
+adapter_tag_info_t aic79xx_tag_info[] =
+{
+       {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+       {{4, 64, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4}},
+       {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+       {{0, 16, 4, 0, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+*/
+
+#ifdef CONFIG_AIC79XX_CMDS_PER_DEVICE
+#define AIC79XX_CMDS_PER_DEVICE CONFIG_AIC79XX_CMDS_PER_DEVICE
+#else
+#define AIC79XX_CMDS_PER_DEVICE AHD_MAX_QUEUE
+#endif
+
+#define AIC79XX_CONFIGED_TAG_COMMANDS {                                        \
+       AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE,               \
+       AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE,               \
+       AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE,               \
+       AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE,               \
+       AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE,               \
+       AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE,               \
+       AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE,               \
+       AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE                \
+}
+
+/*
+ * By default, use the number of commands specified by
+ * the users kernel configuration.
+ */
+static adapter_tag_info_t aic79xx_tag_info[] =
+{
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS},
+       {AIC79XX_CONFIGED_TAG_COMMANDS}
+};
+
+/*
+ * By default, read streaming is disabled.  In theory,
+ * read streaming should enhance performance, but early
+ * U320 drive firmware actually performs slower with
+ * read streaming enabled.
+ */
+#ifdef CONFIG_AIC79XX_ENABLE_RD_STRM
+#define AIC79XX_CONFIGED_RD_STRM 0xFFFF
+#else
+#define AIC79XX_CONFIGED_RD_STRM 0
+#endif
+
+static uint16_t aic79xx_rd_strm_info[] =
+{
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM,
+       AIC79XX_CONFIGED_RD_STRM
+};
+
+/*
+ * There should be a specific return value for this in scsi.h, but
+ * it seems that most drivers ignore it.
+ */
+#define DID_UNDERFLOW   DID_ERROR
+
+void
+ahd_print_path(struct ahd_softc *ahd, struct scb *scb)
+{
+       printk("(scsi%d:%c:%d:%d): ",
+              ahd->platform_data->host->host_no,
+              scb != NULL ? SCB_GET_CHANNEL(ahd, scb) : 'X',
+              scb != NULL ? SCB_GET_TARGET(ahd, scb) : -1,
+              scb != NULL ? SCB_GET_LUN(scb) : -1);
+}
+
+/*
+ * XXX - these options apply unilaterally to _all_ adapters
+ *       cards in the system.  This should be fixed.  Exceptions to this
+ *       rule are noted in the comments.
+ */
+
+/*
+ * Skip the scsi bus reset.  Non 0 make us skip the reset at startup.  This
+ * has no effect on any later resets that might occur due to things like
+ * SCSI bus timeouts.
+ */
+static uint32_t aic79xx_no_reset;
+
+/*
+ * Certain PCI motherboards will scan PCI devices from highest to lowest,
+ * others scan from lowest to highest, and they tend to do all kinds of
+ * strange things when they come into contact with PCI bridge chips.  The
+ * net result of all this is that the PCI card that is actually used to boot
+ * the machine is very hard to detect.  Most motherboards go from lowest
+ * PCI slot number to highest, and the first SCSI controller found is the
+ * one you boot from.  The only exceptions to this are when a controller
+ * has its BIOS disabled.  So, we by default sort all of our SCSI controllers
+ * from lowest PCI slot number to highest PCI slot number.  We also force
+ * all controllers with their BIOS disabled to the end of the list.  This
+ * works on *almost* all computers.  Where it doesn't work, we have this
+ * option.  Setting this option to non-0 will reverse the order of the sort
+ * to highest first, then lowest, but will still leave cards with their BIOS
+ * disabled at the very end.  That should fix everyone up unless there are
+ * really strange cirumstances.
+ */
+static int aic79xx_reverse_scan = 0;
+
+/*
+ * Should we force EXTENDED translation on a controller.
+ *     0 == Use whatever is in the SEEPROM or default to off
+ *     1 == Use whatever is in the SEEPROM or default to on
+ */
+static uint32_t aic79xx_extended = 0;
+
+/*
+ * PCI bus parity checking of the Adaptec controllers.  This is somewhat
+ * dubious at best.  To my knowledge, this option has never actually
+ * solved a PCI parity problem, but on certain machines with broken PCI
+ * chipset configurations, it can generate tons of false error messages.
+ * It's included in the driver for completeness.
+ *   0 = Shut off PCI parity check
+ *  -1 = Normal polarity pci parity checking
+ *   1 = reverse polarity pci parity checking
+ *
+ * NOTE: you can't actually pass -1 on the lilo prompt.  So, to set this
+ * variable to -1 you would actually want to simply pass the variable
+ * name without a number.  That will invert the 0 which will result in
+ * -1.
+ */
+static int aic79xx_pci_parity = 0;
+
+/*
+ * aic79xx_detect() has been run, so register all device arrivals
+ * immediately with the system rather than deferring to the sorted
+ * attachment performed by aic79xx_detect().
+ */
+int aic79xx_detect_complete;
+
+/*
+ * So that we can set how long each device is given as a selection timeout.
+ * The table of values goes like this:
+ *   0 - 256ms
+ *   1 - 128ms
+ *   2 - 64ms
+ *   3 - 32ms
+ * We default to 256ms because some older devices need a longer time
+ * to respond to initial selection.
+ */
+static int aic79xx_seltime = 0x00;
+
+/*
+ * Certain devices do not perform any aging on commands.  Should the
+ * device be saturated by commands in one portion of the disk, it is
+ * possible for transactions on far away sectors to never be serviced.
+ * To handle these devices, we can periodically send an ordered tag to
+ * force all outstanding transactions to be serviced prior to a new
+ * transaction.
+ */
+int aic79xx_periodic_otag;
+
+/*
+ * Module information and settable options.
+ */
+#ifdef MODULE
+static char *aic79xx = NULL;
+/*
+ * Just in case someone uses commas to separate items on the insmod
+ * command line, we define a dummy buffer here to avoid having insmod
+ * write wild stuff into our code segment
+ */
+static char dummy_buffer[60] = "Please don't trounce on me insmod!!\n";
+
+MODULE_AUTHOR("Maintainer: Justin T. Gibbs <gibbs@scsiguy.com>");
+MODULE_DESCRIPTION("Adaptec Aic77XX/78XX SCSI Host Bus Adapter driver");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10)
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+MODULE_PARM(aic79xx, "s");
+MODULE_PARM_DESC(aic79xx, "period delimited, options string.
+       verbose                 Enable verbose/diagnostic logging
+       debug                   Bitmask of debug values to enable
+       no_reset                Supress initial bus resets
+       extended                Enable extended geometry on all controllers
+       periodic_otag           Send an ordered tagged transaction periodically
+                               to prevent tag starvation.  This may be
+                               required by some older disk drives/RAID arrays. 
+       reverse_scan            Sort PCI devices highest Bus/Slot to lowest
+       tag_info:<tag_str>      Set per-target tag depth
+       rd_strm:<rd_strm_masks> Set per-target read streaming setting.
+       seltime:<int>           Selection Timeout(0/256ms,1/128ms,2/64ms,3/32ms)
+
+       Sample /etc/modules.conf line:
+               Enable verbose logging
+               Set tag depth on Controller 2/Target 2 to 10 tags
+               Shorten the selection timeout to 128ms from its default of 256
+
+       options aic79xx='\"verbose.tag_info:{{}.{}.{..10}}.seltime:1\"'
+
+       Sample /etc/modules.conf line:
+               Change Read Streaming for Controller's 2 and 3
+
+       options aic79xx='\"rd_strm:{..0xFFF0.0xC0F0}\"'
+");
+#endif
+
+static void ahd_linux_handle_scsi_status(struct ahd_softc *,
+                                        struct ahd_linux_device *,
+                                        struct scb *);
+static void ahd_linux_filter_command(struct ahd_softc*, Scsi_Cmnd*,
+                                    struct scb*);
+static void ahd_linux_dev_timed_unfreeze(u_long arg);
+#if NO_YET
+static void ahd_linux_sem_timeout(u_long arg);
+static int  ahd_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag);
+#endif 
+static void ahd_linux_initialize_scsi_bus(struct ahd_softc *ahd);
+static void ahd_linux_select_queue_depth(struct Scsi_Host *host,
+                                        Scsi_Device *scsi_devs);
+static u_int ahd_linux_user_tagdepth(struct ahd_softc *ahd,
+                                    struct ahd_devinfo *devinfo);
+static void ahd_linux_device_queue_depth(struct ahd_softc *ahd,
+                                        Scsi_Device *device);
+static struct ahd_linux_target*        ahd_linux_alloc_target(struct ahd_softc*,
+                                                      u_int, u_int);
+static void                    ahd_linux_free_target(struct ahd_softc*,
+                                                     struct ahd_linux_target*);
+static struct ahd_linux_device*        ahd_linux_alloc_device(struct ahd_softc*,
+                                                      struct ahd_linux_target*,
+                                                      u_int);
+static void                    ahd_linux_free_device(struct ahd_softc*,
+                                                     struct ahd_linux_device*);
+static void ahd_linux_run_device_queue(struct ahd_softc*,
+                                      struct ahd_linux_device*);
+static void ahd_linux_setup_tag_info(char *p, char *end);
+static void ahd_linux_setup_rd_strm_info(char *p, char *end);
+static int ahd_linux_next_unit(void);
+static void ahd_runq_tasklet(unsigned long data);
+static int ahd_linux_halt(struct notifier_block *nb, u_long event, void *buf);
+
+static __inline struct ahd_linux_device*
+                    ahd_linux_get_device(struct ahd_softc *ahd, u_int channel,
+                                         u_int target, u_int lun, int alloc);
+static __inline void ahd_linux_queue_cmd_complete(struct ahd_softc *ahd,
+                                                 Scsi_Cmnd *cmd);
+static __inline void ahd_linux_run_complete_queue(struct ahd_softc *ahd,
+                                                 struct ahd_cmd *acmd);
+static __inline void ahd_linux_check_device_queue(struct ahd_softc *ahd,
+                                                 struct ahd_linux_device *dev);
+static __inline struct ahd_linux_device *
+                    ahd_linux_next_device_to_run(struct ahd_softc *ahd);
+static __inline void ahd_linux_run_device_queues(struct ahd_softc *ahd);
+static __inline void ahd_linux_sniff_command(struct ahd_softc*, Scsi_Cmnd*,
+                                            struct scb*);
+static __inline void ahd_linux_unmap_scb(struct ahd_softc*, struct scb*);
+
+static __inline int ahd_linux_map_seg(struct ahd_softc *ahd, struct scb *scb,
+                                     struct ahd_dma_seg *sg,
+                                     bus_addr_t addr, bus_size_t len);
+
+static __inline struct ahd_linux_device*
+ahd_linux_get_device(struct ahd_softc *ahd, u_int channel, u_int target,
+              u_int lun, int alloc)
+{
+       struct ahd_linux_target *targ;
+       struct ahd_linux_device *dev;
+       u_int target_offset;
+
+       target_offset = target;
+       if (channel != 0)
+               target_offset += 8;
+       targ = ahd->platform_data->targets[target_offset];
+       if (targ == NULL) {
+               if (alloc != 0) {
+                       targ = ahd_linux_alloc_target(ahd, channel, target);
+                       if (targ == NULL)
+                               return (NULL);
+               } else
+                       return (NULL);
+       }
+       dev = targ->devices[lun];
+       if (dev == NULL && alloc != 0)
+               dev = ahd_linux_alloc_device(ahd, targ, lun);
+       return (dev);
+}
+
+static __inline void
+ahd_linux_queue_cmd_complete(struct ahd_softc *ahd, Scsi_Cmnd *cmd)
+{
+       /*
+        * Typically, the complete queue has very few entries
+        * queued to it before the queue is emptied by
+        * ahd_linux_run_complete_queue, so sorting the entries
+        * by generation number should be inexpensive.
+        * We perform the sort so that commands that complete
+        * with an error are retuned in the order origionally
+        * queued to the controller so that any subsequent retries
+        * are performed in order.  The underlying ahd routines do
+        * not guarantee the order that aborted commands will be
+        * returned to us.
+        */
+       struct ahd_completeq *completeq;
+       struct ahd_cmd *list_cmd;
+       struct ahd_cmd *acmd;
+
+       /*
+        * If we want the request requeued, make sure there
+        * are sufficent retries.  In the old scsi error code,
+        * we used to be able to specify a result code that
+        * bypassed the retry count.  Now we must use this
+        * hack.
+        */
+       if (cmd->result == (CAM_REQUEUE_REQ << 16))
+               cmd->retries--;
+       completeq = &ahd->platform_data->completeq;
+       list_cmd = TAILQ_FIRST(completeq);
+       acmd = (struct ahd_cmd *)cmd;
+       while (list_cmd != NULL
+           && acmd_scsi_cmd(list_cmd).serial_number
+            < acmd_scsi_cmd(acmd).serial_number)
+               list_cmd = TAILQ_NEXT(list_cmd, acmd_links.tqe);
+       if (list_cmd != NULL)
+               TAILQ_INSERT_BEFORE(list_cmd, acmd, acmd_links.tqe);
+       else
+               TAILQ_INSERT_TAIL(completeq, acmd, acmd_links.tqe);
+}
+
+static __inline void
+ahd_linux_run_complete_queue(struct ahd_softc *ahd, struct ahd_cmd *acmd)
+{      
+       u_long done_flags;
+
+       ahd_done_lock(ahd, &done_flags);
+       while (acmd != NULL) {
+               Scsi_Cmnd *cmd;
+
+               cmd = &acmd_scsi_cmd(acmd);
+               acmd = TAILQ_NEXT(acmd, acmd_links.tqe);
+               cmd->host_scribble = NULL;
+               cmd->scsi_done(cmd);
+       }
+       ahd_done_unlock(ahd, &done_flags);
+}
+
+static __inline void
+ahd_linux_check_device_queue(struct ahd_softc *ahd,
+                            struct ahd_linux_device *dev)
+{
+       if ((dev->flags & AHD_DEV_FREEZE_TIL_EMPTY) != 0
+        && dev->active == 0) {
+               dev->flags &= ~AHD_DEV_FREEZE_TIL_EMPTY;
+               dev->qfrozen--;
+       }
+
+       if (TAILQ_FIRST(&dev->busyq) == NULL
+        || dev->openings == 0 || dev->qfrozen != 0)
+               return;
+
+       ahd_linux_run_device_queue(ahd, dev);
+}
+
+static __inline struct ahd_linux_device *
+ahd_linux_next_device_to_run(struct ahd_softc *ahd)
+{
+       
+       if ((ahd->flags & AHD_RESOURCE_SHORTAGE) != 0
+        || ahd->platform_data->qfrozen != 0)
+               return (NULL);
+       return (TAILQ_FIRST(&ahd->platform_data->device_runq));
+}
+
+static __inline void
+ahd_linux_run_device_queues(struct ahd_softc *ahd)
+{
+       struct ahd_linux_device *dev;
+
+       while ((dev = ahd_linux_next_device_to_run(ahd)) != NULL) {
+               TAILQ_REMOVE(&ahd->platform_data->device_runq, dev, links);
+               dev->flags &= ~AHD_DEV_ON_RUN_LIST;
+               ahd_linux_check_device_queue(ahd, dev);
+       }
+}
+
+static __inline void
+ahd_linux_sniff_command(struct ahd_softc *ahd, Scsi_Cmnd *cmd, struct scb *scb)
+{
+       /*
+        * Determine whether we care to filter
+        * information out of this command.  If so,
+        * pass it on to ahd_linux_filter_command() for more
+        * heavy weight processing.
+        */
+       if (cmd->cmnd[0] == INQUIRY)
+               ahd_linux_filter_command(ahd, cmd, scb);
+}
+
+static __inline void
+ahd_linux_unmap_scb(struct ahd_softc *ahd, struct scb *scb)
+{
+       Scsi_Cmnd *cmd;
+       int direction;
+
+       cmd = scb->io_ctx;
+       direction = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+       ahd_sync_sglist(ahd, scb, BUS_DMASYNC_POSTWRITE);
+       if (cmd->use_sg != 0) {
+               struct scatterlist *sg;
+
+               sg = (struct scatterlist *)cmd->request_buffer;
+               pci_unmap_sg(ahd->dev_softc, sg, cmd->use_sg, direction);
+       } else if (cmd->request_bufflen != 0) {
+               pci_unmap_single(ahd->dev_softc,
+                                scb->platform_data->buf_busaddr,
+                                cmd->request_bufflen, direction);
+       }
+}
+
+static __inline int
+ahd_linux_map_seg(struct ahd_softc *ahd, struct scb *scb,
+                 struct ahd_dma_seg *sg, bus_addr_t addr, bus_size_t len)
+{
+       int      consumed;
+
+       if ((scb->sg_count + 1) > AHD_NSEG)
+               panic("Too few segs for dma mapping.  "
+                     "Increase AHD_NSEG\n");
+
+       consumed = 1;
+       sg->addr = ahd_htole32(addr & 0xFFFFFFFF);
+       scb->platform_data->xfer_len += len;
+       if (sizeof(bus_addr_t) > 4
+        && (ahd->flags & AHD_39BIT_ADDRESSING) != 0) {
+               /*
+                * Due to DAC restrictions, we can't
+                * cross a 4GB boundary.
+                */
+               if ((addr ^ (addr + len - 1)) & ~0xFFFFFFFF) {
+                       struct   ahd_dma_seg *next_sg;
+                       uint32_t next_len;
+
+                       printf("Crossed Seg\n");
+                       if ((scb->sg_count + 2) > AHD_NSEG)
+                               panic("Too few segs for dma mapping.  "
+                                     "Increase AHD_NSEG\n");
+
+                       consumed++;
+                       next_sg = sg + 1;
+                       next_sg->addr = 0;
+                       next_len = 0x100000000 - (addr & 0xFFFFFFFF);
+                       len -= next_len;
+                       next_len |= ((addr >> 8) + 0x1000000) & 0x7F000000;
+                       next_sg->len = ahd_htole32(next_len);
+               }
+               len |= (addr >> 8) & 0x7F000000;
+       }
+       sg->len = ahd_htole32(len);
+       return (consumed);
+}
+
+/**************************** Tasklet Handler *********************************/
+
+static void
+ahd_runq_tasklet(unsigned long data)
+{
+       struct ahd_softc* ahd;
+       struct ahd_linux_device *dev;
+       u_long flags;
+
+       ahd = (struct ahd_softc *)data;
+       ahd_lock(ahd, &flags);
+       while ((dev = ahd_linux_next_device_to_run(ahd)) != NULL) {
+       
+               TAILQ_REMOVE(&ahd->platform_data->device_runq, dev, links);
+               dev->flags &= ~AHD_DEV_ON_RUN_LIST;
+               ahd_linux_check_device_queue(ahd, dev);
+               /* Yeild to our interrupt handler */
+               ahd_unlock(ahd, &flags);
+               ahd_lock(ahd, &flags);
+       }
+       ahd_unlock(ahd, &flags);
+}
+
+/************************ Shutdown/halt/reboot hook ***************************/
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+
+static struct notifier_block ahd_linux_notifier = {
+       ahd_linux_halt, NULL, 0
+};
+
+static int ahd_linux_halt(struct notifier_block *nb, u_long event, void *buf)
+{
+       struct ahd_softc *ahd;
+
+       if (event == SYS_DOWN || event == SYS_HALT) {
+               TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+                       ahd_shutdown(ahd);
+               }
+       }
+       return (NOTIFY_OK);
+}
+
+/******************************** Macros **************************************/
+#define BUILD_SCSIID(ahd, cmd)                                         \
+       ((((cmd)->target << TID_SHIFT) & TID) | (ahd)->our_id)
+
+/******************************** Bus DMA *************************************/
+int
+ahd_dma_tag_create(struct ahd_softc *ahd, bus_dma_tag_t parent,
+                  bus_size_t alignment, bus_size_t boundary,
+                  bus_addr_t lowaddr, bus_addr_t highaddr,
+                  bus_dma_filter_t *filter, void *filterarg,
+                  bus_size_t maxsize, int nsegments,
+                  bus_size_t maxsegsz, int flags, bus_dma_tag_t *ret_tag)
+{
+       bus_dma_tag_t dmat;
+
+       dmat = malloc(sizeof(*dmat), M_DEVBUF, M_NOWAIT);
+       if (dmat == NULL)
+               return (ENOMEM);
+
+       /*
+        * Linux is very simplistic about DMA memory.  For now don't
+        * maintain all specification information.  Once Linux supplies
+        * better facilities for doing these operations, or the
+        * needs of this particular driver change, we might need to do
+        * more here.
+        */
+       dmat->alignment = alignment;
+       dmat->boundary = boundary;
+       dmat->maxsize = maxsize;
+       *ret_tag = dmat;
+       return (0);
+}
+
+void
+ahd_dma_tag_destroy(struct ahd_softc *ahd, bus_dma_tag_t dmat)
+{
+       free(dmat, M_DEVBUF);
+}
+
+int
+ahd_dmamem_alloc(struct ahd_softc *ahd, bus_dma_tag_t dmat, void** vaddr,
+                int flags, bus_dmamap_t *mapp)
+{
+       bus_dmamap_t map;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT);
+       if (map == NULL)
+               return (ENOMEM);
+       /*
+        * Although we can dma data above 4GB, our
+        * "consistent" memory is below 4GB for
+        * space efficiency reasons (only need a 4byte
+        * address).  For this reason, we have to reset
+        * our dma mask when doing allocations.
+        */
+       if (ahd->dev_softc != NULL)
+               ahd_pci_set_dma_mask(ahd->dev_softc, 0xFFFFFFFF);
+       *vaddr = pci_alloc_consistent(ahd->dev_softc,
+                                     dmat->maxsize, &map->bus_addr);
+       if (ahd->dev_softc != NULL)
+               ahd_pci_set_dma_mask(ahd->dev_softc,
+                                    ahd->platform_data->hw_dma_mask);
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) */
+       /*
+        * At least in 2.2.14, malloc is a slab allocator so all
+        * allocations are aligned.  We assume for these kernel versions
+        * that all allocations will be bellow 4Gig, physically contiguous,
+        * and accessable via DMA by the controller.
+        */
+       map = NULL; /* No additional information to store */
+       *vaddr = malloc(dmat->maxsize, M_DEVBUF, M_NOWAIT);
+#endif
+       if (*vaddr == NULL)
+               return (ENOMEM);
+       *mapp = map;
+       return(0);
+}
+
+void
+ahd_dmamem_free(struct ahd_softc *ahd, bus_dma_tag_t dmat,
+               void* vaddr, bus_dmamap_t map)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       pci_free_consistent(ahd->dev_softc, dmat->maxsize,
+                           vaddr, map->bus_addr);
+#else
+       free(vaddr, M_DEVBUF);
+#endif
+}
+
+int
+ahd_dmamap_load(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map,
+               void *buf, bus_size_t buflen, bus_dmamap_callback_t *cb,
+               void *cb_arg, int flags)
+{
+       /*
+        * Assume for now that this will only be used during
+        * initialization and not for per-transaction buffer mapping.
+        */
+       bus_dma_segment_t stack_sg;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       stack_sg.ds_addr = map->bus_addr;
+#else
+#define VIRT_TO_BUS(a) (uint32_t)virt_to_bus((void *)(a))
+       stack_sg.ds_addr = VIRT_TO_BUS(buf);
+#endif
+       stack_sg.ds_len = dmat->maxsize;
+       cb(cb_arg, &stack_sg, /*nseg*/1, /*error*/0);
+       return (0);
+}
+
+void
+ahd_dmamap_destroy(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+       /*
+        * The map may is NULL in our < 2.3.X implementation.
+        */
+       if (map != NULL)
+               free(map, M_DEVBUF);
+}
+
+int
+ahd_dmamap_unload(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+       /* Nothing to do */
+       return (0);
+}
+
+/********************* Platform Dependent Functions ***************************/
+int
+ahd_softc_comp(struct ahd_softc *lahd, struct ahd_softc *rahd)
+{
+       int     value;
+       char    primary_channel;
+
+       /*
+        * Under Linux, cards are ordered as follows:
+        *      1) PCI devices with BIOS enabled sorted by bus/slot/func.
+        *      2) All remaining PCI devices sorted by bus/slot/func.
+        */
+       value = (lahd->flags & AHD_BIOS_ENABLED)
+             - (rahd->flags & AHD_BIOS_ENABLED);
+       if (value != 0)
+               /* Controllers with BIOS enabled have a *higher* priority */
+               return (-value);
+
+       /* Still equal.  Sort by bus/slot/func. */
+       if (aic79xx_reverse_scan != 0)
+               value = ahd_get_pci_bus(rahd->dev_softc)
+                     - ahd_get_pci_bus(lahd->dev_softc);
+       else
+               value = ahd_get_pci_bus(lahd->dev_softc)
+                     - ahd_get_pci_bus(rahd->dev_softc);
+       if (value != 0)
+               return (value);
+       if (aic79xx_reverse_scan != 0)
+               value = ahd_get_pci_slot(rahd->dev_softc)
+                     - ahd_get_pci_slot(lahd->dev_softc);
+       else
+               value = ahd_get_pci_slot(lahd->dev_softc)
+                     - ahd_get_pci_slot(rahd->dev_softc);
+       if (value != 0)
+               return (value);
+
+       /*
+        * On multi-function devices, the user can choose
+        * to have function 1 probed before function 0.
+        * Give whichever channel is the primary channel
+        * the lowest priority.
+        */
+       primary_channel = (lahd->flags & AHD_PRIMARY_CHANNEL) + 'A';
+       value = 1;
+       if (lahd->channel == primary_channel)
+               value = -1;
+       return (value);
+}
+
+static void
+ahd_linux_setup_tag_info(char *p, char *end)
+{
+       char    *base;
+       char    *tok;
+       char    *tok_end;
+       char    *tok_end2;
+       int      i;
+       int      instance;
+       int      targ;
+       int      done;
+       char     tok_list[] = {'.', ',', '{', '}', '\0'};
+
+       if (*p != ':')
+               return;
+
+       instance = -1;
+       targ = -1;
+       done = FALSE;
+       base = p;
+       /* Forward us just past the ':' */
+       tok = base + 1;
+       tok_end = strchr(tok, '\0');
+       if (tok_end < end)
+               *tok_end = ',';
+       while (!done) {
+               switch (*tok) {
+               case '{':
+                       if (instance == -1)
+                               instance = 0;
+                       else if (targ == -1)
+                               targ = 0;
+                       tok++;
+                       break;
+               case '}':
+                       if (targ != -1)
+                               targ = -1;
+                       else if (instance != -1)
+                               instance = -1;
+                       tok++;
+                       break;
+               case ',':
+               case '.':
+                       if (instance == -1)
+                               done = TRUE;
+                       else if (targ >= 0)
+                               targ++;
+                       else if (instance >= 0)
+                               instance++;
+                       if ((targ >= AHD_NUM_TARGETS) ||
+                           (instance >= NUM_ELEMENTS(aic79xx_tag_info)))
+                               done = TRUE;
+                       tok++;
+                       if (!done) {
+                               base = tok;
+                       }
+                       break;
+               case '\0':
+                       done = TRUE;
+                       break;
+               default:
+                       done = TRUE;
+                       tok_end = strchr(tok, '\0');
+                       for (i = 0; tok_list[i]; i++) {
+                               tok_end2 = strchr(tok, tok_list[i]);
+                               if ((tok_end2) && (tok_end2 < tok_end)) {
+                                       tok_end = tok_end2;
+                                       done = FALSE;
+                               }
+                       }
+                       if ((instance >= 0) && (targ >= 0)
+                        && (instance < NUM_ELEMENTS(aic79xx_tag_info))
+                        && (targ < AHD_NUM_TARGETS)) {
+                               aic79xx_tag_info[instance].tag_commands[targ] =
+                                   simple_strtoul(tok, NULL, 0) & 0xff;
+                       }
+                       tok = tok_end;
+                       break;
+               }
+       }
+       while ((p != base) && (p != NULL))
+               p = strtok(NULL, ",.");
+}
+
+static void
+ahd_linux_setup_rd_strm_info(char *p, char *end)
+{
+       char    *base;
+       char    *tok;
+       char    *tok_end;
+       char    *tok_end2;
+       int      i;
+       int      instance;
+       int      targ;
+       int      done;
+       char     tok_list[] = {'.', ',', '{', '}', '\0'};
+
+       if (*p != ':')
+               return;
+
+       instance = -1;
+       targ = -1;
+       done = FALSE;
+       base = p;
+       /* Forward us just past the ':' */
+       tok = base + 1;
+       tok_end = strchr(tok, '\0');
+       if (tok_end < end)
+               *tok_end = ',';
+       while (!done) {
+               switch (*tok) {
+               case '{':
+                       if (instance == -1)
+                               instance = 0;
+                       tok++;
+                       break;
+               case '}':
+                       if (instance != -1)
+                               instance = -1;
+                       tok++;
+                       break;
+               case ',':
+               case '.':
+                       if (instance == -1)
+                               done = TRUE;
+                       else if (instance >= 0)
+                               instance++;
+                       if (instance >= NUM_ELEMENTS(aic79xx_rd_strm_info))
+                               done = TRUE;
+                       tok++;
+                       if (!done) {
+                               base = tok;
+                       }
+                       break;
+               case '\0':
+                       done = TRUE;
+                       break;
+               default:
+                       done = TRUE;
+                       tok_end = strchr(tok, '\0');
+                       for (i = 0; tok_list[i]; i++) {
+                               tok_end2 = strchr(tok, tok_list[i]);
+                               if ((tok_end2) && (tok_end2 < tok_end)) {
+                                       tok_end = tok_end2;
+                                       done = FALSE;
+                               }
+                       }
+                       if ((instance >= 0)
+                        && (instance < NUM_ELEMENTS(aic79xx_tag_info))) {
+                               aic79xx_rd_strm_info[instance] =
+                                   simple_strtoul(tok, NULL, 0) & 0xffff;
+                       }
+                       tok = tok_end;
+                       break;
+               }
+       }
+       while ((p != base) && (p != NULL))
+               p = strtok(NULL, ",.");
+}
+
+/*
+ * Handle Linux boot parameters. This routine allows for assigning a value
+ * to a parameter with a ':' between the parameter and the value.
+ * ie. aic79xx=stpwlev:1,extended
+ */
+int
+aic79xx_setup(char *s)
+{
+       int     i, n;
+       char   *p;
+       char   *end;
+
+       static struct {
+               const char *name;
+               uint32_t *flag;
+       } options[] = {
+               { "extended", &aic79xx_extended },
+               { "no_reset", &aic79xx_no_reset },
+               { "verbose", &aic79xx_verbose },
+#ifdef AHD_DEBUG
+               { "debug", &ahd_debug },
+#endif
+               { "reverse_scan", &aic79xx_reverse_scan },
+               { "periodic_otag", &aic79xx_periodic_otag },
+               { "pci_parity", &aic79xx_pci_parity },
+               { "seltime", &aic79xx_seltime },
+               { "tag_info", NULL },
+               { "rd_strm", NULL }
+       };
+
+       end = strchr(s, '\0');
+
+       for (p = strtok(s, ",."); p; p = strtok(NULL, ",.")) {
+               for (i = 0; i < NUM_ELEMENTS(options); i++) {
+                       n = strlen(options[i].name);
+
+                       if (strncmp(options[i].name, p, n) != 0)
+                               continue;
+
+                       if (strncmp(p, "tag_info", n) == 0) {
+                               ahd_linux_setup_tag_info(p + n, end);
+                       } else if (strncmp(p, "rd_strm", n) == 0) {
+                               ahd_linux_setup_rd_strm_info(p + n, end);
+                       } else if (p[n] == ':') {
+                               *(options[i].flag) =
+                                   simple_strtoul(p + n + 1, NULL, 0);
+                       } else if (!strncmp(p, "verbose", n)) {
+                               *(options[i].flag) = 1;
+                       } else {
+                               *(options[i].flag) = ~(*(options[i].flag));
+                       }
+                       break;
+               }
+       }
+       return 1;
+}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
+__setup("aic79xx=", aic79xx_setup);
+#endif
+
+int aic79xx_verbose;
+
+/*
+ * Try to detect an Adaptec 79XX controller.
+ */
+int
+ahd_linux_detect(Scsi_Host_Template *template)
+{
+       struct  ahd_softc *ahd;
+       int     found;
+
+       /*
+        * It is a bug that the upper layer takes
+        * this lock just prior to calling us.
+        */
+       spin_unlock_irq(&io_request_lock);
+
+       /*
+        * Sanity checking of Linux SCSI data structures so
+        * that some of our hacks^H^H^H^H^Hassumptions aren't
+        * violated.
+        */
+       if (offsetof(struct ahd_cmd_internal, end)
+         > offsetof(struct scsi_cmnd, host_scribble)) {
+               printf("ahd_linux_detect: SCSI data structures changed.\n");
+               printf("ahd_linux_detect: Unable to attach\n");
+               return (0);
+       }
+#ifdef MODULE
+       /*
+        * If we've been passed any parameters, process them now.
+        */
+       if (aic79xx)
+               aic79xx_setup(aic79xx);
+       if (dummy_buffer[0] != 'P')
+               printk(KERN_WARNING
+"aic79xx: Please read the file /usr/src/linux/drivers/scsi/README.aic79xx\n"
+"aic79xx: to see the proper way to specify options to the aic79xx module\n"
+"aic79xx: Specifically, don't use any commas when passing arguments to\n"
+"aic79xx: insmod or else it might trash certain memory areas.\n");
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
+       template->proc_name = "aic79xx";
+#else
+       template->proc_dir = &proc_scsi_aic79xx;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,7)
+       /*
+        * We can only map 16MB per-SG
+        * so create a sector limit of
+        * "16MB" in 2K sectors.
+        */
+       template->max_sectors = 8192;
+#endif
+
+       /*
+        * Initialize our softc list lock prior to
+        * probing for any adapters.
+        */
+       ahd_list_lockinit();
+
+#ifdef CONFIG_PCI
+       ahd_linux_pci_probe(template);
+#endif
+
+       /*
+        * Register with the SCSI layer all
+        * controllers we've found.
+        */
+       spin_lock_irq(&io_request_lock);
+       found = 0;
+       TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+
+               if (ahd_linux_register_host(ahd, template) == 0)
+                       found++;
+       }
+       aic79xx_detect_complete++;
+       return (found);
+}
+
+int
+ahd_linux_register_host(struct ahd_softc *ahd, Scsi_Host_Template *template)
+{
+       char  buf[80];
+       struct Scsi_Host *host;
+       char *new_name;
+       u_long s;
+
+       template->name = ahd->description;
+       host = scsi_register(template, sizeof(struct ahd_softc *));
+       if (host == NULL)
+               return (ENOMEM);
+
+       ahd_lock(ahd, &s);
+       *((struct ahd_softc **)host->hostdata) = ahd;
+       ahd->platform_data->host = host;
+       host->can_queue = AHD_MAX_QUEUE;
+       host->cmd_per_lun = 2;
+       host->sg_tablesize = AHD_NSEG;
+       host->select_queue_depths = ahd_linux_select_queue_depth;
+       host->this_id = ahd->our_id;
+       host->irq = ahd->platform_data->irq;
+       host->max_id = (ahd->features & AHD_WIDE) ? 16 : 8;
+       host->max_lun = AHD_NUM_LUNS;
+       host->max_channel = 0;
+       ahd_set_unit(ahd, ahd_linux_next_unit());
+       sprintf(buf, "scsi%d", host->host_no);
+       new_name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+       if (new_name != NULL) {
+               strcpy(new_name, buf);
+               ahd_set_name(ahd, new_name);
+       }
+       host->unique_id = ahd->unit;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)
+       scsi_set_pci_device(host, ahd->dev_softc);
+#endif
+       ahd_linux_initialize_scsi_bus(ahd);
+       ahd_unlock(ahd, &s);
+       return (0);
+}
+
+uint64_t
+ahd_linux_get_memsize()
+{
+       struct sysinfo si;
+
+       si_meminfo(&si);
+       return (si.totalram << PAGE_SHIFT);
+}
+
+/*
+ * Find the smallest available unit number to use
+ * for a new device.  We don't just use a static
+ * count to handle the "repeated hot-(un)plug"
+ * scenario.
+ */
+static int
+ahd_linux_next_unit()
+{
+       struct ahd_softc *ahd;
+       int unit;
+
+       unit = 0;
+retry:
+       TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+               if (ahd->unit == unit) {
+                       unit++;
+                       goto retry;
+               }
+       }
+       return (unit);
+}
+
+/*
+ * Place the SCSI bus into a known state by either resetting it,
+ * or forcing transfer negotiations on the next command to any
+ * target.
+ */
+void
+ahd_linux_initialize_scsi_bus(struct ahd_softc *ahd)
+{
+       int i;
+       int numtarg;
+
+       i = 0;
+       numtarg = 0;
+
+       if (aic79xx_no_reset != 0)
+               ahd->flags &= ~AHD_RESET_BUS_A;
+
+       if ((ahd->flags & AHD_RESET_BUS_A) != 0)
+               ahd_reset_channel(ahd, 'A', /*initiate_reset*/TRUE);
+       else
+               numtarg = (ahd->features & AHD_WIDE) ? 16 : 8;
+
+       for (; i < numtarg; i++) {
+               struct ahd_devinfo devinfo;
+               struct ahd_initiator_tinfo *tinfo;
+               struct ahd_tmode_tstate *tstate;
+               u_int our_id;
+               u_int target_id;
+               char channel;
+
+               channel = 'A';
+               our_id = ahd->our_id;
+               target_id = i;
+               tinfo = ahd_fetch_transinfo(ahd, channel, our_id,
+                                           target_id, &tstate);
+               tinfo->goal = tinfo->user;
+               /*
+                * Don't try negotiations that require PPR messages
+                * until we successfully retrieve Inquiry data.
+                */
+               tinfo->goal.ppr_options = 0;
+               if (tinfo->goal.transport_version > SCSI_REV_2)
+                       tinfo->goal.transport_version = SCSI_REV_2;
+               ahd_compile_devinfo(&devinfo, our_id, target_id,
+                                  CAM_LUN_WILDCARD, channel, ROLE_INITIATOR);
+               ahd_update_neg_request(ahd, &devinfo, tstate,
+                                      tinfo, /*force*/FALSE);
+       }
+       /* Give the bus some time to recover */
+       if ((ahd->flags & AHD_RESET_BUS_A) != 0) {
+               ahd_freeze_simq(ahd);
+               init_timer(&ahd->platform_data->reset_timer);
+               ahd->platform_data->reset_timer.data = (u_long)ahd;
+               ahd->platform_data->reset_timer.expires =
+                   jiffies + (AIC79XX_RESET_DELAY * HZ)/1000;
+               ahd->platform_data->reset_timer.function =
+                   (ahd_linux_callback_t *)ahd_release_simq;
+               add_timer(&ahd->platform_data->reset_timer);
+       }
+}
+
+int
+ahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg)
+{
+       ahd->platform_data =
+           malloc(sizeof(struct ahd_platform_data), M_DEVBUF, M_NOWAIT);
+       if (ahd->platform_data == NULL)
+               return (ENOMEM);
+       memset(ahd->platform_data, 0, sizeof(struct ahd_platform_data));
+       TAILQ_INIT(&ahd->platform_data->completeq);
+       TAILQ_INIT(&ahd->platform_data->device_runq);
+       ahd->platform_data->irq = AHD_LINUX_NOIRQ;
+       ahd->platform_data->hw_dma_mask = 0xFFFFFFFF;
+       ahd_lockinit(ahd);
+       ahd_done_lockinit(ahd);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       init_MUTEX_LOCKED(&ahd->platform_data->eh_sem);
+#else
+       ahd->platform_data->eh_sem = MUTEX_LOCKED;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+       tasklet_init(&ahd->platform_data->runq_tasklet, ahd_runq_tasklet,
+                    (unsigned long)ahd);
+#endif
+       ahd->seltime = (aic79xx_seltime & 0x3) << 4;
+       
+       if (TAILQ_EMPTY(&ahd_tailq))
+               register_reboot_notifier(&ahd_linux_notifier);
+       return (0);
+}
+
+void
+ahd_platform_free(struct ahd_softc *ahd)
+{
+       if (ahd->platform_data != NULL) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+               tasklet_kill(&ahd->platform_data->runq_tasklet);
+#endif
+               if (ahd->platform_data->host != NULL)
+                       scsi_unregister(ahd->platform_data->host);
+               if (ahd->platform_data->irq != AHD_LINUX_NOIRQ)
+                       free_irq(ahd->platform_data->irq, ahd);
+               if (ahd->tags[0] == BUS_SPACE_PIO
+                && ahd->bshs[0].ioport != 0)
+                       release_region(ahd->bshs[0].ioport, 256);
+               if (ahd->tags[1] == BUS_SPACE_PIO
+                && ahd->bshs[1].ioport != 0)
+                       release_region(ahd->bshs[1].ioport, 256);
+               if (ahd->tags[0] == BUS_SPACE_MEMIO
+                && ahd->bshs[0].maddr != NULL) {
+                       u_long base_addr;
+
+                       base_addr = (u_long)ahd->bshs[0].maddr;
+                       base_addr &= PAGE_MASK;
+                       iounmap((void *)base_addr);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+                       release_mem_region(ahd->platform_data->mem_busaddr,
+                                          0x1000);
+#endif
+               }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+               /* XXX Need an instance detach in the PCI code */
+               if (ahd->dev_softc != NULL)
+                       ahd->dev_softc->driver = NULL;
+#endif
+               free(ahd->platform_data, M_DEVBUF);
+       }
+       if (TAILQ_EMPTY(&ahd_tailq)) {
+               unregister_reboot_notifier(&ahd_linux_notifier);
+#ifdef CONFIG_PCI
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+               pci_unregister_driver(&aic79xx_pci_driver);
+#endif
+#endif
+       }
+}
+
+void
+ahd_platform_freeze_devq(struct ahd_softc *ahd, struct scb *scb)
+{
+       ahd_platform_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb),
+                               SCB_GET_CHANNEL(ahd, scb),
+                               SCB_GET_LUN(scb), SCB_LIST_NULL,
+                               ROLE_UNKNOWN, CAM_REQUEUE_REQ);
+}
+
+void
+ahd_platform_set_tags(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
+                     ahd_queue_alg alg)
+{
+       struct ahd_linux_device *dev;
+       int was_queuing;
+       int now_queuing;
+
+       dev = ahd_linux_get_device(ahd, devinfo->channel - 'A',
+                                  devinfo->target,
+                                  devinfo->lun, /*alloc*/FALSE);
+       if (dev == NULL)
+               return;
+       was_queuing = dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED);
+       now_queuing = alg != AHD_QUEUE_NONE;
+       if ((dev->flags & AHD_DEV_FREEZE_TIL_EMPTY) == 0
+        && (was_queuing != now_queuing)
+        && (dev->active != 0)) {
+               dev->flags |= AHD_DEV_FREEZE_TIL_EMPTY;
+               dev->qfrozen++;
+       }
+
+       dev->flags &= ~(AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED|AHD_DEV_PERIODIC_OTAG);
+       if (now_queuing) {
+               u_int usertags;
+
+               usertags = ahd_linux_user_tagdepth(ahd, devinfo);
+               if (!was_queuing) {
+                       /*
+                        * Start out agressively and allow our
+                        * dynamic queue depth algorithm to take
+                        * care of the rest.
+                        */
+                       dev->maxtags = usertags;
+                       dev->openings = dev->maxtags - dev->active;
+               }
+               if (alg == AHD_QUEUE_TAGGED) {
+                       dev->flags |= AHD_DEV_Q_TAGGED;
+                       if (aic79xx_periodic_otag != 0)
+                               dev->flags |= AHD_DEV_PERIODIC_OTAG;
+               } else
+                       dev->flags |= AHD_DEV_Q_BASIC;
+       } else {
+               /* We can only have one opening. */
+               dev->maxtags = 0;
+               dev->openings =  1 - dev->active;
+       }
+}
+
+int
+ahd_platform_abort_scbs(struct ahd_softc *ahd, int target, char channel,
+                       int lun, u_int tag, role_t role, uint32_t status)
+{
+       int targ;
+       int maxtarg;
+       int maxlun;
+       int clun;
+       int count;
+
+       if (tag != SCB_LIST_NULL)
+               return (0);
+
+       targ = 0;
+       if (target != CAM_TARGET_WILDCARD) {
+               targ = target;
+               maxtarg = targ + 1;
+       } else {
+               maxtarg = (ahd->features & AHD_WIDE) ? 16 : 8;
+       }
+       clun = 0;
+       if (lun != CAM_LUN_WILDCARD) {
+               clun = lun;
+               maxlun = clun + 1;
+       } else {
+               maxlun = AHD_NUM_LUNS;
+       }
+
+       count = 0;
+       for (; targ < maxtarg; targ++) {
+
+               for (; clun < maxlun; clun++) {
+                       struct ahd_linux_device *dev;
+                       struct ahd_busyq *busyq;
+                       struct ahd_cmd *acmd;
+
+                       dev = ahd_linux_get_device(ahd, /*chan*/0, targ,
+                                                  clun, /*alloc*/FALSE);
+                       if (dev == NULL)
+                               continue;
+
+                       busyq = &dev->busyq;
+                       while ((acmd = TAILQ_FIRST(busyq)) != NULL) {
+                               Scsi_Cmnd *cmd;
+
+                               cmd = &acmd_scsi_cmd(acmd);
+                               TAILQ_REMOVE(busyq, acmd,
+                                            acmd_links.tqe);
+                               count++;
+                               cmd->result = status << 16;
+                               ahd_linux_queue_cmd_complete(ahd, cmd);
+                       }
+               }
+       }
+
+       return (count);
+}
+
+/*
+ * Sets the queue depth for each SCSI device hanging
+ * off the input host adapter.
+ */
+static void
+ahd_linux_select_queue_depth(struct Scsi_Host * host,
+                            Scsi_Device * scsi_devs)
+{
+       Scsi_Device *device;
+       struct  ahd_softc *ahd;
+       u_long  flags;
+       int     scbnum;
+
+       ahd = *((struct ahd_softc **)host->hostdata);
+       ahd_lock(ahd, &flags);
+       scbnum = 0;
+       for (device = scsi_devs; device != NULL; device = device->next) {
+               if (device->host == host) {
+                       ahd_linux_device_queue_depth(ahd, device);
+                       scbnum += device->queue_depth;
+               }
+       }
+       ahd_unlock(ahd, &flags);
+}
+
+static u_int
+ahd_linux_user_tagdepth(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
+{
+       static int warned_user;
+       u_int tags;
+
+       tags = 0;
+       if ((ahd->user_discenable & devinfo->target_mask) != 0) {
+               if (warned_user == 0
+                && ahd->unit >= NUM_ELEMENTS(aic79xx_tag_info)) {
+
+                       printf("aic79xx: WARNING, insufficient "
+                              "tag_info instances for installed "
+                              "controllers. Using defaults\n");
+                       printf("aic79xx: Please update the "
+                              "aic79xx_tag_info array in the "
+                              "aic79xx.c source file.\n");
+                       tags = AHD_MAX_QUEUE;
+                       warned_user++;
+               } else {
+                       adapter_tag_info_t *tag_info;
+
+                       tag_info = &aic79xx_tag_info[ahd->unit];
+                       tags = tag_info->tag_commands[devinfo->target_offset];
+                       if (tags > AHD_MAX_QUEUE)
+                               tags = AHD_MAX_QUEUE;
+               }
+       }
+       return (tags);
+}
+
+/*
+ * Determines the queue depth for a given device.
+ */
+static void
+ahd_linux_device_queue_depth(struct ahd_softc *ahd, Scsi_Device * device)
+{
+       struct  ahd_devinfo devinfo;
+       u_int   tags;
+
+       ahd_compile_devinfo(&devinfo,
+                           ahd->our_id,
+                           device->id, device->lun,
+                           device->channel == 0 ? 'A' : 'B',
+                           ROLE_INITIATOR);
+       tags = ahd_linux_user_tagdepth(ahd, &devinfo);
+       if (tags != 0
+        && device->tagged_supported != 0) {
+
+               device->queue_depth = tags;
+               ahd_set_tags(ahd, &devinfo, AHD_QUEUE_TAGGED);
+               printf("scsi%d:%c:%d:%d: Tagged Queuing enabled.  Depth %d\n",
+                      ahd->platform_data->host->host_no, devinfo.channel,
+                      devinfo.target, devinfo.lun, tags);
+       } else {
+               /*
+                * We allow the OS to queue 2 untagged transactions to
+                * us at any time even though we can only execute them
+                * serially on the controller/device.  This should remove
+                * some latency.
+                */
+               device->queue_depth = 2;
+       }
+}
+
+/*
+ * Queue an SCB to the controller.
+ */
+int
+ahd_linux_queue(Scsi_Cmnd * cmd, void (*scsi_done) (Scsi_Cmnd *))
+{
+       struct   ahd_softc *ahd;
+       struct   ahd_linux_device *dev;
+       u_long   flags;
+
+       ahd = *(struct ahd_softc **)cmd->host->hostdata;
+
+       /*
+        * Save the callback on completion function.
+        */
+       cmd->scsi_done = scsi_done;
+
+       ahd_lock(ahd, &flags);
+       dev = ahd_linux_get_device(ahd, cmd->channel, cmd->target,
+                                  cmd->lun, /*alloc*/TRUE);
+       if (dev == NULL) {
+               ahd_unlock(ahd, &flags);
+               printf("aic79xx_linux_queue: Unable to allocate device!\n");
+               return (-ENOMEM);
+       }
+       if (cmd->cmd_len > MAX_CDB_LEN)
+               return (-EINVAL);
+       cmd->result = CAM_REQ_INPROG << 16;
+       TAILQ_INSERT_TAIL(&dev->busyq, (struct ahd_cmd *)cmd, acmd_links.tqe);
+       if ((dev->flags & AHD_DEV_ON_RUN_LIST) == 0) {
+               TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq, dev, links);
+               dev->flags |= AHD_DEV_ON_RUN_LIST;
+               ahd_linux_run_device_queues(ahd);
+       }
+       ahd_unlock(ahd, &flags);
+       return (0);
+}
+
+static void
+ahd_linux_run_device_queue(struct ahd_softc *ahd, struct ahd_linux_device *dev)
+{
+       struct   ahd_cmd *acmd;
+       struct   scsi_cmnd *cmd;
+       struct   scb *scb;
+       struct   hardware_scb *hscb;
+       struct   ahd_initiator_tinfo *tinfo;
+       struct   ahd_tmode_tstate *tstate;
+       uint16_t mask;
+
+       if ((dev->flags & AHD_DEV_ON_RUN_LIST) != 0)
+               panic("running device on run list");
+
+       while ((acmd = TAILQ_FIRST(&dev->busyq)) != NULL
+           && dev->openings > 0 && dev->qfrozen == 0) {
+
+               /*
+                * Schedule us to run later.  The only reason we are not
+                * running is because the whole controller Q is frozen.
+                */
+               if (ahd->platform_data->qfrozen != 0) {
+
+                       TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq,
+                                         dev, links);
+                       dev->flags |= AHD_DEV_ON_RUN_LIST;
+                       return;
+               }
+               /*
+                * Get an scb to use.
+                */
+               if ((scb = ahd_get_scb(ahd)) == NULL) {
+                       TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq,
+                                        dev, links);
+                       dev->flags |= AHD_DEV_ON_RUN_LIST;
+                       ahd->flags |= AHD_RESOURCE_SHORTAGE;
+                       return;
+               }
+               TAILQ_REMOVE(&dev->busyq, acmd, acmd_links.tqe);
+               cmd = &acmd_scsi_cmd(acmd);
+               scb->io_ctx = cmd;
+               scb->platform_data->dev = dev;
+               hscb = scb->hscb;
+               cmd->host_scribble = (char *)scb;
+
+               /*
+                * Fill out basics of the HSCB.
+                */
+               hscb->control = 0;
+               hscb->scsiid = BUILD_SCSIID(ahd, cmd);
+               hscb->lun = cmd->lun;
+               mask = SCB_GET_TARGET_MASK(ahd, scb);
+               tinfo = ahd_fetch_transinfo(ahd, SCB_GET_CHANNEL(ahd, scb),
+                                           SCB_GET_OUR_ID(scb),
+                                           SCB_GET_TARGET(ahd, scb), &tstate);
+
+               if ((ahd->user_discenable & mask) != 0)
+                       hscb->control |= DISCENB;
+
+               if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0)
+                       scb->flags |= SCB_PACKETIZED;
+
+               if ((tstate->auto_negotiate & mask) != 0) {
+                       scb->flags |= SCB_AUTO_NEGOTIATE;
+                       scb->hscb->control |= MK_MESSAGE;
+               }
+
+               if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) != 0) {
+                       if (dev->commands_since_idle_or_otag == AHD_OTAG_THRESH
+                        && (dev->flags & AHD_DEV_Q_TAGGED) != 0) {
+                               hscb->control |= MSG_ORDERED_TASK;
+                               dev->commands_since_idle_or_otag = 0;
+                       } else {
+                               hscb->control |= MSG_SIMPLE_TASK;
+                       }
+               }
+
+               hscb->cdb_len = cmd->cmd_len;
+               memcpy(hscb->shared_data.idata.cdb, cmd->cmnd, hscb->cdb_len);
+
+               scb->sg_count = 0;
+               ahd_set_residual(scb, 0);
+               ahd_set_sense_residual(scb, 0);
+               if (cmd->use_sg != 0) {
+                       void    *sg;
+                       struct   scatterlist *cur_seg;
+                       u_int    nseg;
+                       int      dir;
+
+                       cur_seg = (struct scatterlist *)cmd->request_buffer;
+                       dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+                       nseg = pci_map_sg(ahd->dev_softc, cur_seg,
+                                         cmd->use_sg, dir);
+                       scb->platform_data->xfer_len = 0;
+                       for (sg = scb->sg_list; nseg > 0; nseg--, cur_seg++) {
+                               bus_addr_t addr;
+                               bus_size_t len;
+
+                               addr = sg_dma_address(cur_seg);
+                               len = sg_dma_len(cur_seg);
+                               scb->platform_data->xfer_len += len;
+                               sg = ahd_sg_setup(ahd, scb, sg, addr, len,
+                                                 /*last*/nseg == 1);
+                       }
+               } else if (cmd->request_bufflen != 0) {
+                       void *sg;
+                       bus_addr_t addr;
+                       int dir;
+
+                       sg = scb->sg_list;
+                       dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+                       addr = pci_map_single(ahd->dev_softc,
+                                             cmd->request_buffer,
+                                             cmd->request_bufflen, dir);
+                       scb->platform_data->xfer_len = cmd->request_bufflen;
+                       scb->platform_data->buf_busaddr = addr;
+                       sg = ahd_sg_setup(ahd, scb, sg, addr,
+                                         cmd->request_bufflen, /*last*/TRUE);
+               }
+
+               LIST_INSERT_HEAD(&ahd->pending_scbs, scb, pending_links);
+               dev->openings--;
+               dev->active++;
+               dev->commands_issued++;
+               if ((dev->flags & AHD_DEV_PERIODIC_OTAG) != 0)
+                       dev->commands_since_idle_or_otag++;
+               scb->flags |= SCB_ACTIVE;
+               ahd_queue_scb(ahd, scb);
+       }
+}
+
+/*
+ * SCSI controller interrupt handler.
+ */
+void
+ahd_linux_isr(int irq, void *dev_id, struct pt_regs * regs)
+{
+       struct  ahd_softc *ahd;
+       struct  ahd_cmd *acmd;
+       u_long  flags;
+       struct  ahd_linux_device *next_dev;
+
+       ahd = (struct ahd_softc *) dev_id;
+       ahd_lock(ahd, &flags); 
+       ahd_intr(ahd);
+       acmd = TAILQ_FIRST(&ahd->platform_data->completeq);
+       TAILQ_INIT(&ahd->platform_data->completeq);
+       next_dev = ahd_linux_next_device_to_run(ahd);
+       ahd_unlock(ahd, &flags);
+       if (next_dev) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+               tasklet_schedule(&ahd->platform_data->runq_tasklet);
+#else
+               ahd_runq_tasklet((unsigned long)ahd);
+#endif
+       }
+       if (acmd != NULL)
+               ahd_linux_run_complete_queue(ahd, acmd);
+}
+
+void
+ahd_platform_flushwork(struct ahd_softc *ahd)
+{
+       struct ahd_cmd *acmd;
+
+       acmd = TAILQ_FIRST(&ahd->platform_data->completeq);
+       TAILQ_INIT(&ahd->platform_data->completeq);
+       if (acmd != NULL)
+               ahd_linux_run_complete_queue(ahd, acmd);
+}
+
+static struct ahd_linux_target*
+ahd_linux_alloc_target(struct ahd_softc *ahd, u_int channel, u_int target)
+{
+       struct ahd_linux_target *targ;
+       u_int target_offset;
+
+       targ = malloc(sizeof(*targ), M_DEVBUG, M_NOWAIT);
+       if (targ == NULL)
+               return (NULL);
+       memset(targ, 0, sizeof(*targ));
+       targ->channel = channel;
+       targ->target = target;
+       targ->ahd = ahd;
+       target_offset = target;
+       if (channel != 0)
+               target_offset += 8;
+       ahd->platform_data->targets[target_offset] = targ;
+       return (targ);
+}
+
+static void
+ahd_linux_free_target(struct ahd_softc *ahd, struct ahd_linux_target *targ)
+{
+       u_int target_offset;
+
+       target_offset = targ->target;
+       if (targ->channel != 0)
+               target_offset += 8;
+       ahd->platform_data->targets[target_offset] = NULL;
+       free(targ, M_DEVBUF);
+}
+
+static struct ahd_linux_device*
+ahd_linux_alloc_device(struct ahd_softc *ahd,
+                struct ahd_linux_target *targ, u_int lun)
+{
+       struct ahd_linux_device *dev;
+
+       dev = malloc(sizeof(*dev), M_DEVBUG, M_NOWAIT);
+       if (dev == NULL)
+               return (NULL);
+       memset(dev, 0, sizeof(*dev));
+       init_timer(&dev->timer);
+       TAILQ_INIT(&dev->busyq);
+       dev->flags = AHD_DEV_UNCONFIGURED;
+       dev->lun = lun;
+       dev->target = targ;
+
+       /*
+        * We start out life using untagged
+        * transactions of which we allow one.
+        */
+       dev->openings = 1;
+
+       /*
+        * Set maxtags to 0.  This will be changed if we
+        * later determine that we are dealing with
+        * a tagged queuing capable device.
+        */
+       dev->maxtags = 0;
+       
+       targ->refcount++;
+       targ->devices[lun] = dev;
+       return (dev);
+}
+
+static void
+ahd_linux_free_device(struct ahd_softc *ahd, struct ahd_linux_device *dev)
+{
+       struct ahd_linux_target *targ;
+
+       del_timer(&dev->timer);
+       targ = dev->target;
+       targ->devices[dev->lun] = NULL;
+       free(dev, M_DEVBUF);
+       targ->refcount--;
+       if (targ->refcount == 0)
+               ahd_linux_free_target(ahd, targ);
+}
+
+/*
+ * Return a string describing the driver.
+ */
+const char *
+ahd_linux_info(struct Scsi_Host *host)
+{
+       static char buffer[512];
+       char    ahd_info[256];
+       char   *bp;
+       struct ahd_softc *ahd;
+
+       bp = &buffer[0];
+       ahd = *(struct ahd_softc **)host->hostdata;
+       memset(bp, 0, sizeof(buffer));
+       strcpy(bp, "Adaptec AIC79XX PCI-X SCSI HBA DRIVER, Rev ");
+       strcat(bp, AIC79XX_DRIVER_VERSION);
+       strcat(bp, "\n");
+       strcat(bp, "        <");
+       strcat(bp, ahd->description);
+       strcat(bp, ">\n");
+       strcat(bp, "        ");
+       ahd_controller_info(ahd, ahd_info);
+       strcat(bp, ahd_info);
+       strcat(bp, "\n");
+
+       return (bp);
+}
+
+void
+ahd_send_async(struct ahd_softc *ahd, char channel,
+              u_int target, u_int lun, ac_code code, void *arg)
+{
+       switch (code) {
+       case AC_TRANSFER_NEG:
+       {
+               char    buf[80];
+               struct  ahd_linux_target *targ;
+               struct  info_str info;
+               struct  ahd_initiator_tinfo *tinfo;
+               struct  ahd_tmode_tstate *tstate;
+
+               info.buffer = buf;
+               info.length = sizeof(buf);
+               info.offset = 0;
+               info.pos = 0;
+               tinfo = ahd_fetch_transinfo(ahd, channel, ahd->our_id,
+                                           target, &tstate);
+
+               /*
+                * Don't bother reporting results while
+                * negotiations are still pending.
+                */
+               if (tinfo->curr.period != tinfo->goal.period
+                || tinfo->curr.width != tinfo->goal.width
+                || tinfo->curr.offset != tinfo->goal.offset
+                || tinfo->curr.ppr_options != tinfo->goal.ppr_options)
+                       if (bootverbose == 0)
+                               break;
+
+               /*
+                * Don't bother reporting results that
+                * are identical to those last reported.
+                */
+               targ = ahd->platform_data->targets[target];
+               if (targ == NULL)
+                       break;
+               if (tinfo->curr.period == targ->last_tinfo.period
+                && tinfo->curr.width == targ->last_tinfo.width
+                && tinfo->curr.offset == targ->last_tinfo.offset
+                && tinfo->curr.ppr_options == targ->last_tinfo.ppr_options)
+                       if (bootverbose == 0)
+                               break;
+
+               targ->last_tinfo.period = tinfo->curr.period;
+               targ->last_tinfo.width = tinfo->curr.width;
+               targ->last_tinfo.offset = tinfo->curr.offset;
+               targ->last_tinfo.ppr_options = tinfo->curr.ppr_options;
+
+               printf("(%s:%c:", ahd_name(ahd), channel);
+               if (target == CAM_TARGET_WILDCARD)
+                       printf("*): ");
+               else
+                       printf("%d): ", target);
+               ahd_format_transinfo(&info, &tinfo->curr);
+               if (info.pos < info.length)
+                       *info.buffer = '\0';
+               else
+                       buf[info.length - 1] = '\0';
+               printf("%s", buf);
+               break;
+       }
+        case AC_SENT_BDR:
+               break;
+        case AC_BUS_RESET:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+               if (ahd->platform_data->host != NULL) {
+                       scsi_report_bus_reset(ahd->platform_data->host,
+                                             channel - 'A');
+               }
+#endif
+                break;
+        default:
+                panic("ahd_send_async: Unexpected async event");
+        }
+}
+
+/*
+ * Calls the higher level scsi done function and frees the scb.
+ */
+void
+ahd_done(struct ahd_softc *ahd, struct scb * scb)
+{
+       Scsi_Cmnd *cmd;
+       struct ahd_linux_device *dev;
+
+       LIST_REMOVE(scb, pending_links);
+       if ((scb->flags & SCB_UNTAGGEDQ) != 0) {
+               struct scb_tailq *untagged_q;
+               int target_offset;
+
+               target_offset = SCB_GET_TARGET_OFFSET(ahd, scb);
+               untagged_q = &(ahd->untagged_queues[target_offset]);
+               TAILQ_REMOVE(untagged_q, scb, links.tqe);
+               ahd_run_untagged_queue(ahd, untagged_q);
+       }
+
+       if ((scb->flags & SCB_ACTIVE) == 0) {
+               printf("SCB %d done'd twice\n", scb->hscb->tag);
+               ahd_dump_card_state(ahd);
+               panic("Stopping for safety");
+       }
+       cmd = scb->io_ctx;
+       dev = scb->platform_data->dev;
+       dev->active--;
+       dev->openings++;
+       ahd_linux_unmap_scb(ahd, scb);
+       if (scb->flags & SCB_SENSE) {
+               memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+               memcpy(cmd->sense_buffer, ahd_get_sense_buf(ahd, scb),
+                      MIN(sizeof(struct scsi_sense_data),
+                          sizeof(cmd->sense_buffer)));
+               cmd->result |= (DRIVER_SENSE << 24);
+       } else if (scb->flags & SCB_PKT_SENSE) {
+               struct scsi_status_iu_header *siu;
+               u_int sense_len;
+
+               /*
+                * Copy only the sense data into the provided buffer.
+                */
+               siu = (struct scsi_status_iu_header *)scb->sense_data;
+               sense_len = MIN(scsi_4btoul(siu->sense_length),
+                               sizeof(cmd->sense_buffer));
+               memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+               memcpy(cmd->sense_buffer,
+                      ahd_get_sense_buf(ahd, scb) + SIU_SENSE_OFFSET(siu),
+                      sense_len);
+
+#ifdef AHD_DEBUG
+               if (ahd_debug & AHD_SHOW_SENSE) {
+                       int i;
+
+                       printf("Copied %d bytes of sense data offset %d:",
+                              sense_len, SIU_SENSE_OFFSET(siu));
+                       for (i = 0; i < sense_len; i++)
+                               printf(" 0x%x", cmd->sense_buffer[i]);
+                       printf("\n");
+               }
+#endif
+               cmd->result |= (DRIVER_SENSE << 24);
+       } else {
+               /*
+                * Guard against stale sense data.
+                * The Linux mid-layer assumes that sense
+                * was retrieved anytime the first byte of
+                * the sense buffer looks "sane".
+                */
+               cmd->sense_buffer[0] = 0;
+       }
+       if (ahd_get_transaction_status(scb) == CAM_REQ_INPROG) {
+               uint32_t amount_xferred;
+
+               amount_xferred =
+                   ahd_get_transfer_length(scb) - ahd_get_residual(scb);
+               if (amount_xferred < scb->io_ctx->underflow) {
+                       printf("Saw underflow (%ld of %ld bytes). "
+                              "Treated as error\n",
+                               ahd_get_residual(scb),
+                               ahd_get_transfer_length(scb));
+                       ahd_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+               } else {
+                       ahd_set_transaction_status(scb, CAM_REQ_CMP);
+                       ahd_linux_sniff_command(ahd, cmd, scb);
+               }
+       } else if (ahd_get_transaction_status(scb) == DID_OK) {
+               ahd_linux_handle_scsi_status(ahd, dev, scb);
+       } else if (ahd_get_transaction_status(scb) == DID_NO_CONNECT) {
+               /*
+                * Should a selection timeout kill the device?
+                * That depends on whether the selection timeout
+                * is persistent.  Since we have no guarantee that
+                * the mid-layer will issue an inquiry for this device
+                * again, we can't just kill it off.
+               dev->flags |= AHD_DEV_UNCONFIGURED;
+                */
+       }
+
+       if (dev->openings == 1
+        && ahd_get_transaction_status(scb) == CAM_REQ_CMP
+        && ahd_get_scsi_status(scb) != SCSI_STATUS_QUEUE_FULL)
+               dev->tag_success_count++;
+       /*
+        * Some devices deal with temporary internal resource
+        * shortages by returning queue full.  When the queue
+        * full occurrs, we throttle back.  Slowly try to get
+        * back to our previous queue depth.
+        */
+       if ((dev->openings + dev->active) < dev->maxtags
+        && dev->tag_success_count > AHD_TAG_SUCCESS_INTERVAL) {
+               dev->tag_success_count = 0;
+               dev->openings++;
+       }
+
+       if (dev->active == 0)
+               dev->commands_since_idle_or_otag = 0;
+
+       if (TAILQ_EMPTY(&dev->busyq)) {
+               if ((dev->flags & AHD_DEV_UNCONFIGURED) != 0
+                && dev->active == 0)
+                       ahd_linux_free_device(ahd, dev);
+       } else if ((dev->flags & AHD_DEV_ON_RUN_LIST) == 0) {
+               TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq, dev, links);
+               dev->flags |= AHD_DEV_ON_RUN_LIST;
+       }
+
+       if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
+               printf("Recovery SCB completes\n");
+               up(&ahd->platform_data->eh_sem);
+       }
+
+       ahd_free_scb(ahd, scb);
+       ahd_linux_queue_cmd_complete(ahd, cmd);
+}
+
+static void
+ahd_linux_handle_scsi_status(struct ahd_softc *ahd,
+                            struct ahd_linux_device *dev, struct scb *scb)
+{
+       /*
+        * We don't currently trust the mid-layer to
+        * properly deal with queue full or busy.  So,
+        * when one occurs, we tell the mid-layer to
+        * unconditionally requeue the command to us
+        * so that we can retry it ourselves.  We also
+        * implement our own throttling mechanism so
+        * we don't clobber the device with too many
+        * commands.
+        */
+       switch (ahd_get_scsi_status(scb)) {
+       default:
+               break;
+       case SCSI_STATUS_QUEUE_FULL:
+       {
+               /*
+                * By the time the core driver has returned this
+                * command, all other commands that were queued
+                * to us but not the device have been returned.
+                * This ensures that dev->active is equal to
+                * the number of commands actually queued to
+                * the device.
+                */
+               dev->tag_success_count = 0;
+               if (dev->active != 0) {
+                       /*
+                        * Drop our opening count to the number
+                        * of commands currently outstanding.
+                        */
+                       dev->openings = 0;
+#ifdef AHD_DEBUG
+                       if (ahd_debug & AHD_SHOW_QFULL) {
+                               ahd_print_path(ahd, scb);
+                               printf("Dropping tag count to %d\n",
+                                      dev->active);
+                       }
+#endif
+                       if (dev->active == dev->tags_on_last_queuefull) {
+
+                               dev->last_queuefull_same_count++;
+                               /*
+                                * If we repeatedly see a queue full
+                                * at the same queue depth, this
+                                * device has a fixed number of tag
+                                * slots.  Lock in this tag depth
+                                * so we stop seeing queue fulls from
+                                * this device.
+                                */
+                               if (dev->last_queuefull_same_count
+                                == AHD_LOCK_TAGS_COUNT) {
+                                       dev->maxtags = dev->active;
+                                       ahd_print_path(ahd, scb);
+                                       printf("Locking max tag count at %d\n",
+                                              dev->active);
+                               }
+                       } else {
+                               dev->tags_on_last_queuefull = dev->active;
+                               dev->last_queuefull_same_count = 0;
+                       }
+                       ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
+                       ahd_set_scsi_status(scb, SCSI_STATUS_OK);
+                       break;
+               }
+               /*
+                * Drop down to a single opening, and treat this
+                * as if the target return BUSY SCSI status.
+                */
+               dev->openings = 1;
+               /* FALLTHROUGH */
+       }
+       case SCSI_STATUS_BUSY:
+               /*
+                * Set a short timer to defer sending commands for
+                * a bit since Linux will not delay in this case.
+                */
+               if ((dev->flags & AHD_DEV_TIMER_ACTIVE) != 0) {
+                       printf("%s:%c:%d: Device Timer still active during "
+                              "busy processing\n", ahd_name(ahd),
+                               dev->target->channel, dev->target->target);
+                       break;
+               }
+               dev->flags |= AHD_DEV_TIMER_ACTIVE;
+               dev->qfrozen++;
+               init_timer(&dev->timer);
+               dev->timer.data = (u_long)dev;
+               dev->timer.expires = jiffies + (HZ/2);
+               dev->timer.function = ahd_linux_dev_timed_unfreeze;
+               add_timer(&dev->timer);
+               break;
+       }
+}
+
+static void
+ahd_linux_filter_command(struct ahd_softc *ahd, Scsi_Cmnd *cmd, struct scb *scb)
+{
+       switch (cmd->cmnd[0]) {
+       case INQUIRY:
+       {
+               struct  ahd_devinfo devinfo;
+               struct  scsi_inquiry *inq;
+               struct  scsi_inquiry_data *sid;
+               struct  ahd_initiator_tinfo *tinfo;
+               struct  ahd_transinfo *user;
+               struct  ahd_transinfo *goal;
+               struct  ahd_transinfo *curr;
+               struct  ahd_tmode_tstate *tstate;
+               struct  ahd_linux_device *dev;
+               u_int   scsiid;
+               int     transferred_len;
+               int     minlen;
+               int     was_configured;
+               u_int   width;
+               u_int   period;
+               u_int   offset;
+               u_int   ppr_options;
+               u_int   trans_version;
+               u_int   prot_version;
+               static  int warned_user;
+
+                /*
+                 * Validate the command.  We only want to filter
+                 * standard inquiry commands, not those querying
+                 * Vital Product Data.
+                 */
+               inq = (struct scsi_inquiry *)cmd->cmnd;
+               if ((inq->byte2 & SI_EVPD) != 0
+                || inq->page_code != 0)
+                       break;
+
+               if (cmd->use_sg != 0) {
+                       printf("%s: SG Inquiry response ignored\n",
+                              ahd_name(ahd));
+                       break;
+               }
+               transferred_len = ahd_get_transfer_length(scb)
+                               - ahd_get_residual(scb);
+               sid = (struct scsi_inquiry_data *)cmd->request_buffer;
+
+               /*
+                * Determine if this lun actually exists.  If so,
+                * hold on to its corresponding device structure.
+                * If not, make sure we release the device and
+                * don't bother processing the rest of this inquiry
+                * command.
+                */
+               dev = ahd_linux_get_device(ahd, cmd->channel,
+                                          cmd->target, cmd->lun,
+                                          /*alloc*/FALSE);
+               was_configured = dev->flags & AHD_DEV_UNCONFIGURED;
+               if (transferred_len >= 1
+                && SID_QUAL(sid) == SID_QUAL_LU_CONNECTED) {
+
+                       dev->flags &= ~AHD_DEV_UNCONFIGURED;
+               } else {
+                       dev->flags |= AHD_DEV_UNCONFIGURED;
+                       break;
+               }
+
+               /*
+                * Update our notion of this device's transfer
+                * negotiation capabilities.
+                */
+               scsiid = BUILD_SCSIID(ahd, cmd);
+               ahd_compile_devinfo(&devinfo, SCSIID_OUR_ID(scsiid),
+                                   cmd->target, cmd->lun,
+                                   SCSIID_CHANNEL(ahd, scsiid),
+                                   ROLE_INITIATOR);
+               tinfo = ahd_fetch_transinfo(ahd, devinfo.channel,
+                                           devinfo.our_scsiid,
+                                           devinfo.target, &tstate);
+               user = &tinfo->user;
+               goal = &tinfo->goal;
+               curr = &tinfo->curr;
+               width = user->width;
+               period = user->period;
+               offset = user->offset;
+               ppr_options = user->ppr_options;
+               trans_version = user->transport_version;
+               prot_version = user->protocol_version;
+               /*
+                * If we have read streaming info for this controller,
+                * apply it to this target.
+                */
+               if (warned_user == 0
+                && ahd->unit >= NUM_ELEMENTS(aic79xx_rd_strm_info)) {
+
+                       printf("aic79xx: WARNING, insufficient "
+                              "rd_strm instances for installed "
+                              "controllers. Using defaults\n");
+                       printf("aic79xx: Please update the "
+                              "aic79xx_rd_strm_info array in the "
+                              "aic79xx.c source file.\n");
+                       warned_user++;
+               } else {
+                       uint16_t rd_strm_mask;
+
+                       rd_strm_mask = aic79xx_rd_strm_info[ahd->unit];
+                       if ((rd_strm_mask & devinfo.target_mask) == 0)
+                               ppr_options &= ~MSG_EXT_PPR_RD_STRM;
+               }
+               
+               minlen = offsetof(struct scsi_inquiry_data, version) + 1;
+               if (transferred_len >= minlen) {
+                       prot_version = SID_ANSI_REV(sid);
+
+                       /*
+                        * Only attempt SPI3 once we've verified that
+                        * the device claims to support SPI3 features.
+                        */
+                       if (prot_version < SCSI_REV_2)
+                               trans_version = SID_ANSI_REV(sid);
+                       else
+                               trans_version = SCSI_REV_2;
+               }
+
+               minlen = offsetof(struct scsi_inquiry_data, flags) + 1;
+               if (transferred_len >= minlen
+                && (sid->additional_length + 4) >= minlen) {
+                       if ((sid->flags & SID_WBus16) == 0)
+                               width = MSG_EXT_WDTR_BUS_8_BIT;
+                       if ((sid->flags & SID_Sync) == 0) {
+                               period = 0;
+                               offset = 0;
+                               ppr_options = 0;
+                       }
+               } else {
+                       /* Keep current settings */
+                       break;
+               }
+               minlen = offsetof(struct scsi_inquiry_data, spi3data) + 1;
+               /*
+                * This is a kludge to deal with inquiry requests that
+                * are not large enough for us to pull the spi3/4 bits.
+                * In this case, we assume that a device that tells us
+                * they can provide inquiry data that spans the SPI3
+                * bits and says its SCSI3 can handle a PPR request.
+                * If the inquiry request has sufficient buffer space to
+                * cover SPI3 bits, we honor them regardless of reported
+                * SCSI REV.  We also allow any device that has had its
+                * goal ppr_options set to allow DT speeds to keep that
+                * option if a short inquiry occurs that would fail the
+                * normal tests outlined above.
+                */
+               if ((sid->additional_length + 4) >= minlen) {
+                       if (transferred_len >= minlen) {
+                                if ((sid->spi3data & SID_SPI_CLOCK_DT) == 0)
+                                       ppr_options = 0;
+                                if ((sid->spi3data & SID_SPI_IUS) == 0)
+                                       ppr_options &= (MSG_EXT_PPR_DT_REQ
+                                                     | MSG_EXT_PPR_QAS_REQ);
+                       } else if (was_configured
+                               && (curr->transport_version
+                                == user->transport_version)) {
+
+                               /* Keep already existant settings. */
+                               break;
+                       } else if ((goal->ppr_options & MSG_EXT_PPR_DT_REQ)== 0)
+                               ppr_options = 0;
+
+                       if (curr->protocol_version > SCSI_REV_2)
+                               trans_version = user->transport_version;
+               } else {
+                       ppr_options = 0;
+               }
+               ahd_validate_width(ahd, /*tinfo limit*/NULL, &width,
+                                  ROLE_UNKNOWN);
+
+               ahd_find_syncrate(ahd, &period, &ppr_options, AHD_SYNCRATE_MAX);
+               ahd_validate_offset(ahd, /*tinfo limit*/NULL, period,
+                                   &offset, width, ROLE_UNKNOWN);
+               if (offset == 0 || period == 0) {
+                       period = 0;
+                       offset = 0;
+                       ppr_options = 0;
+               }
+               /* Apply our filtered user settings. */
+               curr->transport_version = trans_version;
+               curr->protocol_version = prot_version;
+               ahd_set_width(ahd, &devinfo, width,
+                             AHD_TRANS_GOAL, /*paused*/FALSE);
+               ahd_set_syncrate(ahd, &devinfo, period, offset, ppr_options,
+                                AHD_TRANS_GOAL, /*paused*/FALSE);
+               break;
+       }
+       default:
+               panic("ahd_linux_filter_command: Unexpected Command type  %x\n",
+                     cmd->cmnd[0]);
+               break;
+       }
+}
+
+void
+ahd_freeze_simq(struct ahd_softc *ahd)
+{
+       ahd->platform_data->qfrozen++;
+       if (ahd->platform_data->qfrozen == 1)
+               scsi_block_requests(ahd->platform_data->host);
+}
+
+void
+ahd_release_simq(struct ahd_softc *ahd)
+{
+       u_long s;
+       int    unblock_reqs;
+
+       unblock_reqs = 0;
+       ahd_lock(ahd, &s);
+       if (ahd->platform_data->qfrozen > 0)
+               ahd->platform_data->qfrozen--;
+       if (ahd->platform_data->qfrozen == 0) {
+               unblock_reqs = 1;
+       }
+       ahd_unlock(ahd, &s);
+       /*
+        * There is still a race here.  The mid-layer
+        * should keep its own freeze count and use
+        * a bottom half handler to run the queues
+        * so we can unblock with our own lock held.
+        */
+       if (unblock_reqs) {
+               scsi_unblock_requests(ahd->platform_data->host);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+               tasklet_schedule(&ahd->platform_data->runq_tasklet);
+#else
+               ahd_runq_tasklet((unsigned long)ahd);
+#endif
+       }
+}
+
+#if NOT_YET
+static void
+ahd_linux_sem_timeout(u_long arg)
+{
+       struct semaphore *sem;
+
+       sem = (struct semaphore *)arg;
+       up(sem);
+}
+
+static int
+ahd_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
+{
+       struct ahd_softc *ahd;
+       struct ahd_cmd *acmd;
+       struct ahd_cmd *list_acmd;
+       struct ahd_linux_device *dev;
+       struct scb *pending_scb;
+       u_long s;
+       u_int  saved_scbptr;
+       u_int  active_scb_index;
+       u_int  last_phase;
+       int    retval;
+       int    paused;
+       int    wait;
+       int    disconnected;
+
+       paused = FALSE;
+       wait = FALSE;
+       ahd = *(struct ahd_softc **)cmd->host->hostdata;
+       acmd = (struct ahd_cmd *)cmd;
+
+       printf("%s:%d:%d:%d: Attempting to queue a%s message\n",
+              ahd_name(ahd), cmd->channel, cmd->target, cmd->lun,
+              flag == SCB_ABORT ? "n ABORT" : " TARGET RESET");
+
+       /*
+        * It is a bug that the upper layer takes
+        * this lock just prior to calling us.
+        */
+       spin_unlock_irq(&io_request_lock);
+
+       ahd_lock(ahd, &s);
+
+       /*
+        * First determine if we currently own this command.
+        * Start by searching the device queue.  If not found
+        * there, check the pending_scb list.  If not found
+        * at all, and the system wanted us to just abort the
+        * command return success.
+        */
+       dev = ahd_linux_get_device(ahd, cmd->channel, cmd->target,
+                                  cmd->lun, /*alloc*/FALSE);
+
+       if (dev == NULL) {
+               /*
+                * No target device for this command exists,
+                * so we must not still own the command.
+                */
+               printf("%s:%d:%d:%d: Is not an active device\n",
+                      ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
+               retval = SUCCESS;
+               goto no_cmd;
+       }
+
+       TAILQ_FOREACH(list_acmd, &dev->busyq, acmd_links.tqe) {
+               if (list_acmd == acmd)
+                       break;
+       }
+
+       if (list_acmd != NULL) {
+               printf("%s:%d:%d:%d: Command found on device queue\n",
+                      ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
+               if (flag == SCB_ABORT) {
+                       TAILQ_REMOVE(&dev->busyq, list_acmd, acmd_links.tqe);
+                       cmd->result = DID_ABORT << 16;
+                       ahd_linux_queue_cmd_complete(ahd, cmd);
+                       retval = SUCCESS;
+                       goto done;
+               }
+       }
+
+       /*
+        * See if we can find a matching cmd in the pending list.
+        */
+       LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) {
+               if (pending_scb->io_ctx == cmd)
+                       break;
+       }
+
+       if (pending_scb == NULL && flag == SCB_DEVICE_RESET) {
+
+               /* Any SCB for this device will do for a target reset */
+               LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) {
+                       if (ahd_match_scb(ahd, pending_scb, cmd->target,
+                                         cmd->channel, CAM_LUN_WILDCARD,
+                                         SCB_LIST_NULL, ROLE_INITIATOR) == 0)
+                               break;
+               }
+       }
+
+       if (pending_scb == NULL) {
+               printf("%s:%d:%d:%d: Command not found\n",
+                      ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
+               goto no_cmd;
+       }
+
+       if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) {
+               /*
+                * We can't queue two recovery actions using the same SCB
+                */
+               retval = FAILED;
+               goto  done;
+       }
+
+       /*
+        * Ensure that the card doesn't do anything
+        * behind our back.  Also make sure that we
+        * didn't "just" miss an interrupt that would
+        * affect this cmd.
+        */
+       ahd->flags |= AHD_ALL_INTERRUPTS;
+       do {
+               ahd_intr(ahd);
+               ahd_pause(ahd);
+               ahd_clear_critical_section(ahd);
+       } while (ahd_inb(ahd, INTSTAT) & INT_PEND);
+       ahd->flags &= ~AHD_ALL_INTERRUPTS;
+       paused = TRUE;
+
+       ahd_dump_card_state(ahd);
+
+       if ((pending_scb->flags & SCB_ACTIVE) == 0) {
+               printf("%s:%d:%d:%d: Command already completed\n",
+                      ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
+               goto no_cmd;
+       }
+
+       disconnected = TRUE;
+       if (flag == SCB_ABORT) {
+               if (ahd_search_qinfifo(ahd, cmd->target, cmd->channel + 'A',
+                                      cmd->lun, pending_scb->hscb->tag,
+                                      ROLE_INITIATOR, CAM_REQ_ABORTED,
+                                      SEARCH_COMPLETE) > 0) {
+                       printf("%s:%d:%d:%d: Cmd aborted from QINFIFO\n",
+                              ahd_name(ahd), cmd->channel, cmd->target,
+                                       cmd->lun);
+                       retval = SUCCESS;
+                       goto done;
+               }
+       } else if (ahd_search_qinfifo(ahd, cmd->target, cmd->channel + 'A',
+                                     cmd->lun, pending_scb->hscb->tag,
+                                     ROLE_INITIATOR, /*status*/0,
+                                     SEARCH_COUNT) > 0) {
+               disconnected = FALSE;
+       }
+
+       /*
+        * At this point, pending_scb is the scb associated with the
+        * passed in command.  That command is currently active on the
+        * bus, is in the disconnected state, or we're hoping to find
+        * a command for the same target active on the bus to abuse to
+        * send a BDR.  Queue the appropriate message based on which of
+        * these states we are in.
+        */
+       last_phase = ahd_inb(ahd, LASTPHASE);
+       saved_scbptr = ahd_inb(ahd, SCBPTR);
+       active_scb_index = ahd_inb(ahd, SCB_TAG);
+       if (last_phase != P_BUSFREE
+        && (pending_scb->hscb->tag == active_scb_index
+         || (flag == SCB_DEVICE_RESET
+          && SCSIID_TARGET(ahd, ahd_inb(ahd, SAVED_SCSIID)) == cmd->target))) {
+
+               /*
+                * We're active on the bus, so assert ATN
+                * and hope that the target responds.
+                */
+               pending_scb = ahd_lookup_scb(ahd, active_scb_index);
+               pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+               ahd_outb(ahd, MSG_OUT, HOST_MSG);
+               ahd_outb(ahd, SCSISIGO, last_phase|ATNO);
+               printf("%s:%d:%d:%d: Device is active, asserting ATN\n",
+                      ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
+               wait = TRUE;
+       } else if (disconnected) {
+
+               /*
+                * Actually re-queue this SCB in an attempt
+                * to select the device before it reconnects.
+                * In either case (selection or reselection),
+                * we will now issue the approprate message
+                * to the timed-out device.
+                *
+                * Set the MK_MESSAGE control bit indicating
+                * that we desire to send a message.  We
+                * also set the disconnected flag since
+                * in the paging case there is no guarantee
+                * that our SCB control byte matches the
+                * version on the card.  We don't want the
+                * sequencer to abort the command thinking
+                * an unsolicited reselection occurred.
+                */
+               pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
+               pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+
+               /*
+                * In the non-paging case, the sequencer will
+                * never re-reference the in-core SCB.
+                * To make sure we are notified during
+                * reslection, set the MK_MESSAGE flag in
+                * the card's copy of the SCB.
+                */
+               ahd_outb(ahd, SCBPTR, pending_scb->hscb->tag);
+               ahd_outb(ahd, SCB_CONTROL,
+                        ahd_inb(ahd, SCB_CONTROL)|MK_MESSAGE);
+
+               /*
+                * Clear out any entries in the QINFIFO first
+                * so we are the next SCB for this target
+                * to run.
+                */
+               ahd_search_qinfifo(ahd, cmd->target, cmd->channel + 'A',
+                                  cmd->lun, SCB_LIST_NULL, ROLE_INITIATOR,
+                                  CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+               ahd_print_path(ahd, pending_scb);
+               printf("Queuing a recovery SCB\n");
+               ahd_qinfifo_requeue_tail(ahd, pending_scb);
+               ahd_outb(ahd, SCBPTR, saved_scbptr);
+               printf("%s:%d:%d:%d: Device is disconnected, re-queuing SCB\n",
+                      ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
+               wait = TRUE;
+       } else {
+               printf("%s:%d:%d:%d: Unable to deliver message\n",
+                      ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
+               retval = FAILED;
+               goto done;
+       }
+
+no_cmd:
+       /*
+        * Our assumption is that if we don't have the command, no
+        * recovery action was required, so we return success.  Again,
+        * the semantics of the mid-layer recovery engine are not
+        * well defined, so this may change in time.
+        */
+       retval = SUCCESS;
+done:
+       if (paused)
+               ahd_unpause(ahd);
+       if (wait) {
+               struct timer_list timer;
+               int ret;
+
+               ahd_unlock(ahd, &s);
+               init_timer(&timer);
+               timer.data = (u_long)&ahd->platform_data->eh_sem;
+               timer.expires = jiffies + (5 * HZ);
+               timer.function = ahd_linux_sem_timeout;
+               add_timer(&timer);
+               printf("Recovery code sleeping\n");
+               down(&ahd->platform_data->eh_sem);
+               printf("Recovery code awake\n");
+               ret = del_timer(&timer);
+               if (ret == 0) {
+                       printf("Timer Expired\n");
+                       retval = FAILED;
+               }
+               ahd_lock(ahd, &s);
+       }
+       acmd = TAILQ_FIRST(&ahd->platform_data->completeq);
+       TAILQ_INIT(&ahd->platform_data->completeq);
+       ahd_unlock(ahd, &s);
+       if (acmd != NULL)
+               ahd_linux_run_complete_queue(ahd, acmd);
+       ahd_runq_tasklet((unsigned long)ahd);
+       spin_lock_irq(&io_request_lock);
+       return (retval);
+}
+#endif
+
+static void
+ahd_linux_dev_timed_unfreeze(u_long arg)
+{
+       struct ahd_linux_device *dev;
+       struct ahd_softc *ahd;
+       u_long s;
+
+       dev = (struct ahd_linux_device *)arg;
+       ahd = dev->target->ahd;
+       ahd_lock(ahd, &s);
+       dev->flags &= ~AHD_DEV_TIMER_ACTIVE;
+       if (dev->qfrozen > 0)
+               dev->qfrozen--;
+       if (dev->qfrozen == 0
+        && (dev->flags & AHD_DEV_ON_RUN_LIST) == 0)
+               ahd_linux_run_device_queue(ahd, dev);
+       ahd_unlock(ahd, &s);
+}
+
+/*
+ * Abort the current SCSI command(s).
+ */
+int
+ahd_linux_abort(Scsi_Cmnd *cmd)
+{
+       struct ahd_softc *ahd;
+       u_long s;
+#if NOTYET
+       struct ahd_cmd *acmd;
+       int    found;
+#endif
+
+       ahd = *(struct ahd_softc **)cmd->host->hostdata;
+#if NOTYET
+       int error;
+
+       error = ahd_linux_queue_recovery_cmd(cmd, SCB_ABORT);
+       if (error != 0)
+               printf("aic79xx_abort returns 0x%x\n", error);
+       return (error);
+#else
+       printf("Abort called for cmd %p\n", cmd);
+       ahd_lock(ahd, &s);
+       ahd_dump_card_state(ahd);
+       ahd_unlock(ahd, &s);
+       return (FAILED);
+#endif
+}
+
+/*
+ * Attempt to send a target reset message to the device that timed out.
+ */
+int
+ahd_linux_dev_reset(Scsi_Cmnd *cmd)
+{
+       struct ahd_softc *ahd;
+#if NOTYET
+       struct ahd_cmd *acmd;
+       u_long s;
+       int    found;
+#endif
+
+       printf("dev reset called for cmd %p\n", cmd);
+       ahd = *(struct ahd_softc **)cmd->host->hostdata;
+#if NOTYET
+       int error;
+
+       error = ahd_linux_queue_recovery_cmd(cmd, SCB_DEVICE_RESET);
+       if (error != 0)
+               printf("aic79xx_dev_reset returns 0x%x\n", error);
+       return (error);
+#else
+       return (FAILED);
+#endif
+}
+
+/*
+ * Reset the SCSI bus.
+ */
+int
+ahd_linux_bus_reset(Scsi_Cmnd *cmd)
+{
+       struct ahd_softc *ahd;
+       struct ahd_cmd *acmd;
+       u_long s;
+       int    found;
+
+       printf("bus reset called for cmd %p\n", cmd);
+       /*
+        * It is a bug that the upper layer takes
+        * this lock just prior to calling us.
+        */
+       spin_unlock_irq(&io_request_lock);
+
+       ahd = *(struct ahd_softc **)cmd->host->hostdata;
+       ahd_lock(ahd, &s);
+       found = ahd_reset_channel(ahd, cmd->channel + 'A',
+                                 /*initiate reset*/TRUE);
+       acmd = TAILQ_FIRST(&ahd->platform_data->completeq);
+       TAILQ_INIT(&ahd->platform_data->completeq);
+       ahd_unlock(ahd, &s);
+       if (bootverbose)
+               printf("%s: SCSI bus reset delivered. "
+                      "%d SCBs aborted.\n", ahd_name(ahd), found);
+
+       if (acmd != NULL)
+               ahd_linux_run_complete_queue(ahd, acmd);
+
+       spin_lock_irq(&io_request_lock);
+       return (SUCCESS);
+}
+
+/*
+ * Return the disk geometry for the given SCSI device.
+ */
+int
+ahd_linux_biosparam(Disk *disk, kdev_t dev, int geom[])
+{
+       int     heads;
+       int     sectors;
+       int     cylinders;
+       int     ret;
+       int     extended;
+       struct  ahd_softc *ahd;
+       struct  buffer_head *bh;
+
+       ahd = *((struct ahd_softc **)disk->device->host->hostdata);
+       bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, 1024);
+
+       if (bh) {
+               ret = scsi_partsize(bh, disk->capacity,
+                                   &geom[2], &geom[0], &geom[1]);
+               brelse(bh);
+               if (ret != -1)
+                       return (ret);
+       }
+       heads = 64;
+       sectors = 32;
+       cylinders = disk->capacity / (heads * sectors);
+
+       if (aic79xx_extended != 0)
+               extended = 1;
+       else
+               extended = (ahd->flags & AHD_EXTENDED_TRANS_A) != 0;
+       if (extended && cylinders >= 1024) {
+               heads = 255;
+               sectors = 63;
+               cylinders = disk->capacity / (heads * sectors);
+       }
+       geom[0] = heads;
+       geom[1] = sectors;
+       geom[2] = cylinders;
+       return (0);
+}
+
+/*
+ * Free the passed in Scsi_Host memory structures prior to unloading the
+ * module.
+ */
+int
+ahd_linux_release(struct Scsi_Host * host)
+{
+       struct ahd_softc *ahd;
+       u_long l;
+
+       ahd_list_lock(&l);
+       if (host != NULL) {
+
+               /*
+                * We should be able to just perform
+                * the free directly, but check our
+                * list for extra sanity.
+                */
+               ahd = ahd_find_softc(*(struct ahd_softc **)host->hostdata);
+               if (ahd != NULL) {
+                       u_long s;
+
+                       ahd_lock(ahd, &s);
+                       ahd_intr_enable(ahd, FALSE);
+                       ahd_unlock(ahd, &s);
+                       ahd_free(ahd);
+               }
+       }
+       ahd_list_unlock(&l);
+       return (0);
+}
+
+void
+ahd_platform_dump_card_state(struct ahd_softc *ahd)
+{
+       struct ahd_linux_device *dev;
+       int target;
+       int maxtarget;
+       int lun;
+       int i;
+
+       maxtarget = (ahd->features & AHD_WIDE) ? 15 : 7;
+       for (target = 0; target <=maxtarget; target++) {
+
+               for (lun = 0; lun < AHD_NUM_LUNS; lun++) {
+                       struct ahd_cmd *acmd;
+
+                       dev = ahd_linux_get_device(ahd, 0, target,
+                                                  lun, /*alloc*/FALSE);
+                       if (dev == NULL)
+                               continue;
+
+                       printf("DevQ(%d:%d:%d): ", 0, target, lun);
+                       i = 0;
+                       TAILQ_FOREACH(acmd, &dev->busyq, acmd_links.tqe) {
+                               if (i++ > AHD_SCB_MAX)
+                                       break;
+                       }
+                       printf("%d waiting\n", i);
+               }
+       }
+}
+
+#if defined(MODULE) || LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+static Scsi_Host_Template driver_template = AIC79XX;
+Scsi_Host_Template *aic79xx_driver_template = &driver_template;
+#include "../scsi_module.c"
+#endif
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_osm.h b/xen/drivers/scsi/aic7xxx/aic79xx_osm.h
new file mode 100644 (file)
index 0000000..1c81a58
--- /dev/null
@@ -0,0 +1,1272 @@
+/*
+ * Adaptec AIC79xx device driver for Linux.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.h#35 $
+ *
+ */
+#ifndef _AIC79XX_LINUX_H_
+#define _AIC79XX_LINUX_H_
+
+#include <linux/types.h>
+#include <linux/blk.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/version.h>
+#ifndef AHD_MODVERSION_FILE
+#define __NO_VERSION__
+#endif
+#include <linux/module.h>
+#include <asm/byteorder.h>
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#include <linux/interrupt.h> /* For tasklet support. */
+#include <linux/config.h>
+#include <linux/slab.h>
+#else
+#include <linux/malloc.h>
+#endif
+
+/* Core SCSI definitions */
+#include "../scsi.h"
+#include "../hosts.h"
+
+/* Name space conflict with BSD queue macros */
+#ifdef LIST_HEAD
+#undef LIST_HEAD
+#endif
+
+#include "cam.h"
+#include "queue.h"
+#include "scsi_message.h"
+#include "scsi_iu.h"
+
+/*********************************** Debugging ********************************/
+#ifdef CONFIG_AIC79XX_DEBUG_ENABLE
+#ifdef CONFIG_AIC79XX_DEBUG_MASK
+#define AHD_DEBUG CONFIG_AIC79XX_DEBUG_MASK
+#else
+/*
+ * Compile in debugging code, but do not enable any printfs.
+ */
+#define AHD_DEBUG 0
+#endif
+#endif
+
+/********************************** Misc Macros *******************************/
+#define        roundup(x, y)   ((((x)+((y)-1))/(y))*(y))
+#define        powerof2(x)     ((((x)-1)&(x))==0)
+
+/************************* Forward Declarations *******************************/
+struct ahd_softc;
+typedef struct pci_dev *ahd_dev_softc_t;
+typedef Scsi_Cmnd      *ahd_io_ctx_t;
+
+/******************************* Byte Order ***********************************/
+#define ahd_htobe16(x) cpu_to_be16(x)
+#define ahd_htobe32(x) cpu_to_be32(x)
+#define ahd_htobe64(x) cpu_to_be64(x)
+#define ahd_htole16(x) cpu_to_le16(x)
+#define ahd_htole32(x) cpu_to_le32(x)
+#define ahd_htole64(x) cpu_to_le64(x)
+
+#define ahd_be16toh(x) be16_to_cpu(x)
+#define ahd_be32toh(x) be32_to_cpu(x)
+#define ahd_be64toh(x) be64_to_cpu(x)
+#define ahd_le16toh(x) le16_to_cpu(x)
+#define ahd_le32toh(x) le32_to_cpu(x)
+#define ahd_le64toh(x) le64_to_cpu(x)
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+#ifndef BYTE_ORDER
+#if defined(__BIG_ENDIAN)
+#define BYTE_ORDER BIG_ENDIAN
+#endif
+#if defined(__LITTLE_ENDIAN)
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#endif /* BYTE_ORDER */
+
+/************************* Configuration Data *********************************/
+extern int aic79xx_detect_complete;
+extern Scsi_Host_Template* aic79xx_driver_template;
+
+/***************************** Bus Space/DMA **********************************/
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,17)
+typedef dma_addr_t bus_addr_t;
+#else
+typedef uint32_t bus_addr_t;
+#endif
+typedef uint32_t bus_size_t;
+
+typedef enum {
+       BUS_SPACE_MEMIO,
+       BUS_SPACE_PIO
+} bus_space_tag_t;
+
+typedef union {
+       u_long            ioport;
+       volatile uint8_t *maddr;
+} bus_space_handle_t;
+
+typedef struct bus_dma_segment
+{
+       bus_addr_t      ds_addr;
+       bus_size_t      ds_len;
+} bus_dma_segment_t;
+
+struct ahd_linux_dma_tag
+{
+       bus_size_t      alignment;
+       bus_size_t      boundary;
+       bus_size_t      maxsize;
+};
+typedef struct ahd_linux_dma_tag* bus_dma_tag_t;
+
+struct ahd_linux_dmamap
+{
+       bus_addr_t      bus_addr;
+};
+typedef struct ahd_linux_dmamap* bus_dmamap_t;
+
+typedef int bus_dma_filter_t(void*, bus_addr_t);
+typedef void bus_dmamap_callback_t(void *, bus_dma_segment_t *, int, int);
+
+#define BUS_DMA_WAITOK         0x0
+#define BUS_DMA_NOWAIT         0x1
+#define BUS_DMA_ALLOCNOW       0x2
+#define BUS_DMA_LOAD_SEGS      0x4     /*
+                                        * Argument is an S/G list not
+                                        * a single buffer.
+                                        */
+
+#define BUS_SPACE_MAXADDR      0xFFFFFFFF
+#define BUS_SPACE_MAXADDR_32BIT        0xFFFFFFFF
+#define BUS_SPACE_MAXSIZE_32BIT        0xFFFFFFFF
+
+int    ahd_dma_tag_create(struct ahd_softc *, bus_dma_tag_t /*parent*/,
+                          bus_size_t /*alignment*/, bus_size_t /*boundary*/,
+                          bus_addr_t /*lowaddr*/, bus_addr_t /*highaddr*/,
+                          bus_dma_filter_t*/*filter*/, void */*filterarg*/,
+                          bus_size_t /*maxsize*/, int /*nsegments*/,
+                          bus_size_t /*maxsegsz*/, int /*flags*/,
+                          bus_dma_tag_t */*dma_tagp*/);
+
+void   ahd_dma_tag_destroy(struct ahd_softc *, bus_dma_tag_t /*tag*/);
+
+int    ahd_dmamem_alloc(struct ahd_softc *, bus_dma_tag_t /*dmat*/,
+                        void** /*vaddr*/, int /*flags*/,
+                        bus_dmamap_t* /*mapp*/);
+
+void   ahd_dmamem_free(struct ahd_softc *, bus_dma_tag_t /*dmat*/,
+                       void* /*vaddr*/, bus_dmamap_t /*map*/);
+
+void   ahd_dmamap_destroy(struct ahd_softc *, bus_dma_tag_t /*tag*/,
+                          bus_dmamap_t /*map*/);
+
+int    ahd_dmamap_load(struct ahd_softc *ahd, bus_dma_tag_t /*dmat*/,
+                       bus_dmamap_t /*map*/, void * /*buf*/,
+                       bus_size_t /*buflen*/, bus_dmamap_callback_t *,
+                       void */*callback_arg*/, int /*flags*/);
+
+int    ahd_dmamap_unload(struct ahd_softc *, bus_dma_tag_t, bus_dmamap_t);
+
+/*
+ * Operations performed by ahd_dmamap_sync().
+ */
+#define BUS_DMASYNC_PREREAD    0x01    /* pre-read synchronization */
+#define BUS_DMASYNC_POSTREAD   0x02    /* post-read synchronization */
+#define BUS_DMASYNC_PREWRITE   0x04    /* pre-write synchronization */
+#define BUS_DMASYNC_POSTWRITE  0x08    /* post-write synchronization */
+
+/*
+ * XXX
+ * ahd_dmamap_sync is only used on buffers allocated with
+ * the pci_alloc_consistent() API.  Although I'm not sure how
+ * this works on architectures with a write buffer, Linux does
+ * not have an API to sync "coherent" memory.  Perhaps we need
+ * to do an mb()?
+ */
+#define ahd_dmamap_sync(ahd, dma_tag, dmamap, offset, len, op)
+
+/************************** SCSI Constants/Structures *************************/
+#define SCSI_REV_2 2
+#define        SCSI_STATUS_OK                  0x00
+#define        SCSI_STATUS_CHECK_COND          0x02
+#define        SCSI_STATUS_COND_MET            0x04
+#define        SCSI_STATUS_BUSY                0x08
+#define SCSI_STATUS_INTERMED           0x10
+#define SCSI_STATUS_INTERMED_COND_MET  0x14
+#define SCSI_STATUS_RESERV_CONFLICT    0x18
+#define SCSI_STATUS_CMD_TERMINATED     0x22
+#define SCSI_STATUS_QUEUE_FULL         0x28
+
+/*
+ * 6 byte request sense CDB format.
+ */
+struct scsi_sense
+{
+       uint8_t opcode;
+       uint8_t byte2;
+       uint8_t unused[2];
+       uint8_t length;
+       uint8_t control;
+};
+
+struct scsi_sense_data
+{
+       uint8_t error_code;
+       uint8_t segment;
+       uint8_t flags;
+       uint8_t info[4];
+       uint8_t extra_len;
+       uint8_t cmd_spec_info[4];
+       uint8_t add_sense_code;
+       uint8_t add_sense_code_qual;
+       uint8_t fru;
+       uint8_t sense_key_spec[3];
+       uint8_t extra_bytes[14];
+};
+
+struct scsi_inquiry
+{ 
+       u_int8_t opcode;
+       u_int8_t byte2;
+#define        SI_EVPD 0x01
+       u_int8_t page_code;
+       u_int8_t reserved;
+       u_int8_t length;
+       u_int8_t control;
+};
+
+struct scsi_inquiry_data
+{
+       uint8_t device;
+#define        SID_TYPE(inq_data) ((inq_data)->device & 0x1f)
+#define        SID_QUAL(inq_data) (((inq_data)->device & 0xE0) >> 5)
+#define        SID_QUAL_LU_CONNECTED   0x00    /*
+                                        * The specified peripheral device
+                                        * type is currently connected to
+                                        * logical unit.  If the target cannot
+                                        * determine whether or not a physical
+                                        * device is currently connected, it
+                                        * shall also use this peripheral
+                                        * qualifier when returning the INQUIRY
+                                        * data.  This peripheral qualifier
+                                        * does not mean that the device is
+                                        * ready for access by the initiator.
+                                        */
+#define        SID_QUAL_LU_OFFLINE     0x01    /*
+                                        * The target is capable of supporting
+                                        * the specified peripheral device type
+                                        * on this logical unit; however, the
+                                        * physical device is not currently
+                                        * connected to this logical unit.
+                                        */
+#define SID_QUAL_RSVD          0x02
+#define        SID_QUAL_BAD_LU         0x03    /*
+                                        * The target is not capable of
+                                        * supporting a physical device on
+                                        * this logical unit. For this
+                                        * peripheral qualifier the peripheral
+                                        * device type shall be set to 1Fh to
+                                        * provide compatibility with previous
+                                        * versions of SCSI. All other
+                                        * peripheral device type values are
+                                        * reserved for this peripheral
+                                        * qualifier.
+                                        */
+#define        SID_QUAL_IS_VENDOR_UNIQUE(inq_data) ((SID_QUAL(inq_data) & 0x08) != 0)
+       uint8_t dev_qual2;
+#define        SID_QUAL2       0x7F
+#define        SID_IS_REMOVABLE(inq_data) (((inq_data)->dev_qual2 & 0x80) != 0)
+       uint8_t version;
+#define SID_ANSI_REV(inq_data) ((inq_data)->version & 0x07)
+#define                SCSI_REV_0              0
+#define                SCSI_REV_CCS            1
+#define                SCSI_REV_2              2
+#define                SCSI_REV_SPC            3
+#define                SCSI_REV_SPC2           4
+
+#define SID_ECMA       0x38
+#define SID_ISO                0xC0
+       uint8_t response_format;
+#define SID_AENC       0x80
+#define SID_TrmIOP     0x40
+       uint8_t additional_length;
+       uint8_t reserved[2];
+       uint8_t flags;
+#define        SID_SftRe       0x01
+#define        SID_CmdQue      0x02
+#define        SID_Linked      0x08
+#define        SID_Sync        0x10
+#define        SID_WBus16      0x20
+#define        SID_WBus32      0x40
+#define        SID_RelAdr      0x80
+#define SID_VENDOR_SIZE   8
+       char     vendor[SID_VENDOR_SIZE];
+#define SID_PRODUCT_SIZE  16
+       char     product[SID_PRODUCT_SIZE];
+#define SID_REVISION_SIZE 4
+       char     revision[SID_REVISION_SIZE];
+       /*
+        * The following fields were taken from SCSI Primary Commands - 2
+        * (SPC-2) Revision 14, Dated 11 November 1999
+        */
+#define        SID_VENDOR_SPECIFIC_0_SIZE      20
+       u_int8_t vendor_specific0[SID_VENDOR_SPECIFIC_0_SIZE];
+       /*
+        * An extension of SCSI Parallel Specific Values
+        */
+#define        SID_SPI_IUS             0x01
+#define        SID_SPI_QAS             0x02
+#define        SID_SPI_CLOCK_ST        0x00
+#define        SID_SPI_CLOCK_DT        0x04
+#define        SID_SPI_CLOCK_DT_ST     0x0C
+#define        SID_SPI_MASK            0x0F
+       uint8_t spi3data;
+       uint8_t reserved2;
+       /*
+        * Version Descriptors, stored 2 byte values.
+        */
+       uint8_t version1[2];
+       uint8_t version2[2];
+       uint8_t version3[2];
+       uint8_t version4[2];
+       uint8_t version5[2];
+       uint8_t version6[2];
+       uint8_t version7[2];
+       uint8_t version8[2];
+
+       uint8_t reserved3[22];
+
+#define        SID_VENDOR_SPECIFIC_1_SIZE      160
+       uint8_t vendor_specific1[SID_VENDOR_SPECIFIC_1_SIZE];
+};
+
+static __inline uint32_t scsi_4btoul(uint8_t *bytes);
+static __inline uint32_t
+scsi_4btoul(uint8_t *bytes)
+{
+       return ((bytes[0] << 24)|(bytes[1] << 16)|(bytes[2] << 8)|bytes[3]);
+}
+/************************** Timer DataStructures ******************************/
+typedef struct timer_list ahd_timer_t;
+
+/********************************** Includes **********************************/
+/* Host template and function declarations referenced by the template. */
+#include "aic79xx_host.h"
+
+/* Core driver definitions */
+#include "aic79xx.h"
+
+/***************************** Timer Facilities *******************************/
+#define ahd_timer_init init_timer
+#define ahd_timer_stop del_timer
+typedef void ahd_linux_callback_t (u_long);  
+static __inline void
+ahd_timer_reset(ahd_timer_t *timer, int usec, ahd_callback_t *func, void *arg)
+{
+       struct ahd_softc *ahd;
+
+       ahd = (struct ahd_softc *)arg;
+       del_timer(timer);
+       timer->data = (u_long)arg;
+       timer->expires = jiffies + (usec * HZ)/1000000;
+       timer->function = (ahd_linux_callback_t*)func;
+       add_timer(timer);
+}
+
+/***************************** SMP support ************************************/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,17)
+#include <linux/spinlock.h>
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93)
+#include <linux/smp.h>
+#endif
+
+#define AIC79XX_DRIVER_VERSION  "1.0.0"
+
+/**************************** Front End Queues ********************************/
+/*
+ * Data structure used to cast the Linux struct scsi_cmnd to something
+ * that allows us to use the queue macros.  The linux structure has
+ * plenty of space to hold the links fields as required by the queue
+ * macros, but the queue macors require them to have the correct type.
+ */
+struct ahd_cmd_internal {
+       /* Area owned by the Linux scsi layer. */
+       uint8_t private[offsetof(struct scsi_cmnd, SCp.Status)];
+       union {
+               STAILQ_ENTRY(ahd_cmd)   ste;
+               LIST_ENTRY(ahd_cmd)     le;
+               TAILQ_ENTRY(ahd_cmd)    tqe;
+       } links;
+       uint32_t                        end;
+};
+
+struct ahd_cmd {
+       union {
+               struct ahd_cmd_internal icmd;
+               struct scsi_cmnd        scsi_cmd;
+       } un;
+};
+
+#define acmd_icmd(cmd) ((cmd)->un.icmd)
+#define acmd_scsi_cmd(cmd) ((cmd)->un.scsi_cmd)
+#define acmd_links un.icmd.links
+
+/*************************** Device Data Structures ***************************/
+/*
+ * A per probed device structure used to deal with some error recovery
+ * scenarios that the Linux mid-layer code just doesn't know how to
+ * handle.  The structure allocated for a device only becomes persistant
+ * after a successfully completed inquiry command to the target when
+ * that inquiry data indicates a lun is present.
+ */
+TAILQ_HEAD(ahd_busyq, ahd_cmd);
+typedef enum {
+       AHD_DEV_UNCONFIGURED     = 0x01,
+       AHD_DEV_FREEZE_TIL_EMPTY = 0x02, /* Freeze queue until active == 0 */
+       AHD_DEV_TIMER_ACTIVE     = 0x04, /* Our timer is active */
+       AHD_DEV_ON_RUN_LIST      = 0x08, /* Queued to be run later */
+       AHD_DEV_Q_BASIC          = 0x10, /* Allow basic device queuing */
+       AHD_DEV_Q_TAGGED         = 0x20, /* Allow full SCSI2 command queueing */
+       AHD_DEV_PERIODIC_OTAG    = 0x40  /* Send OTAG to prevent starvation */
+} ahd_dev_flags;
+
+struct ahd_linux_target;
+struct ahd_linux_device {
+       TAILQ_ENTRY(ahd_linux_device) links;
+       struct                  ahd_busyq busyq;
+
+       /*
+        * The number of transactions currently
+        * queued to the device.
+        */
+       int                     active;
+
+       /*
+        * The currently allowed number of 
+        * transactions that can be queued to
+        * the device.  Must be signed for
+        * conversion from tagged to untagged
+        * mode where the device may have more
+        * than one outstanding active transaction.
+        */
+       int                     openings;
+
+       /*
+        * A positive count indicates that this
+        * device's queue is halted.
+        */
+       u_int                   qfrozen;
+       
+       /*
+        * Cumulative command counter.
+        */
+       u_long                  commands_issued;
+
+       /*
+        * The number of tagged transactions when
+        * running at our current opening level
+        * that have been successfully received by
+        * this device since the last QUEUE FULL.
+        */
+       u_int                   tag_success_count;
+#define AHD_TAG_SUCCESS_INTERVAL 50
+
+       ahd_dev_flags           flags;
+
+       /*
+        * Per device timer.
+        */
+       struct timer_list       timer;
+
+       /*
+        * The high limit for the tags variable.
+        */
+       u_int                   maxtags;
+
+       /*
+        * The computed number of tags outstanding
+        * at the time of the last QUEUE FULL event.
+        */
+       u_int                   tags_on_last_queuefull;
+
+       /*
+        * How many times we have seen a queue full
+        * with the same number of tags.  This is used
+        * to stop our adaptive queue depth algorithm
+        * on devices with a fixed number of tags.
+        */
+       u_int                   last_queuefull_same_count;
+#define AHD_LOCK_TAGS_COUNT 50
+
+       /*
+        * How many transactions have been queued
+        * without the device going idle.  We use
+        * this statistic to determine when to issue
+        * an ordered tag to prevent transaction
+        * starvation.  This statistic is only updated
+        * if the AHD_DEV_PERIODIC_OTAG flag is set
+        * on this device.
+        */
+       u_int                   commands_since_idle_or_otag;
+#define AHD_OTAG_THRESH        500
+
+       int                     lun;
+       struct                  ahd_linux_target *target;
+};
+
+struct ahd_linux_target {
+       struct  ahd_linux_device *devices[AHD_NUM_LUNS];
+       int     channel;
+       int     target;
+       int     refcount;
+       struct  ahd_transinfo last_tinfo;
+       struct  ahd_softc *ahd;
+};
+
+/********************* Definitions Required by the Core ***********************/
+/*
+ * Number of SG segments we require.  So long as the S/G segments for
+ * a particular transaction are allocated in a physically contiguous
+ * manner and are allocated below 4GB, the number of S/G segments is
+ * unrestricted.
+ */
+#define        AHD_NSEG 128
+
+/*
+ * Per-SCB OSM storage.
+ */
+struct scb_platform_data {
+       struct ahd_linux_device *dev;
+       bus_addr_t               buf_busaddr;
+       uint32_t                 xfer_len;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+       uint32_t                 resid;         /* Transfer residual */
+#endif
+};
+
+/*
+ * Define a structure used for each host adapter.  All members are
+ * aligned on a boundary >= the size of the member to honor the
+ * alignment restrictions of the various platforms supported by
+ * this driver.
+ */
+TAILQ_HEAD(ahd_completeq, ahd_cmd);
+struct ahd_platform_data {
+       /*
+        * Fields accessed from interrupt context.
+        */
+       struct ahd_linux_target *targets[AHD_NUM_TARGETS]; 
+       TAILQ_HEAD(, ahd_linux_device) device_runq;
+       struct ahd_completeq     completeq;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+       spinlock_t               spin_lock;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+       struct tasklet_struct    runq_tasklet;
+#endif
+       u_int                    qfrozen;
+       struct timer_list        reset_timer;
+       struct semaphore         eh_sem;
+       struct Scsi_Host        *host;          /* pointer to scsi host */
+#define AHD_LINUX_NOIRQ        ((uint32_t)~0)
+       uint32_t                 irq;           /* IRQ for this adapter */
+       uint32_t                 bios_address;
+       uint32_t                 mem_busaddr;   /* Mem Base Addr */
+       bus_addr_t               hw_dma_mask;
+};
+
+/************************** OS Utility Wrappers *******************************/
+#define printf printk
+#define M_NOWAIT GFP_ATOMIC
+#define M_WAITOK 0
+#define malloc(size, type, flags) kmalloc(size, flags)
+#define free(ptr, type) kfree(ptr)
+
+static __inline void ahd_delay(long);
+static __inline void
+ahd_delay(long usec)
+{
+       /*
+        * udelay on Linux can have problems for
+        * multi-millisecond waits.  Wait at most
+        * 1024us per call.
+        */
+       while (usec > 0) {
+               udelay(usec % 1024);
+               usec -= 1024;
+       }
+}
+
+
+/***************************** Low Level I/O **********************************/
+#if defined(__powerpc__) || defined(__i386__) || defined(__ia64__)
+#define MMAPIO
+#endif
+
+static __inline uint8_t ahd_inb(struct ahd_softc * ahd, long port);
+static __inline uint16_t ahd_inw_atomic(struct ahd_softc * ahd, long port);
+static __inline void ahd_outb(struct ahd_softc * ahd, long port, uint8_t val);
+static __inline void ahd_outw_atomic(struct ahd_softc * ahd,
+                                    long port, uint16_t val);
+static __inline void ahd_outsb(struct ahd_softc * ahd, long port,
+                              uint8_t *, int count);
+static __inline void ahd_insb(struct ahd_softc * ahd, long port,
+                              uint8_t *, int count);
+
+static __inline uint8_t
+ahd_inb(struct ahd_softc * ahd, long port)
+{
+       uint8_t x;
+#ifdef MMAPIO
+
+       if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+               x = readb(ahd->bshs[0].maddr + port);
+       } else {
+               x = inb(ahd->bshs[(port) >> 8].ioport + ((port) & 0xFF));
+       }
+#else
+       x = inb(ahd->bshs[(port) >> 8].ioport + ((port) & 0xFF));
+#endif
+       mb();
+       return (x);
+}
+
+static __inline uint16_t
+ahd_inw_atomic(struct ahd_softc * ahd, long port)
+{
+       uint8_t x;
+#ifdef MMAPIO
+
+       if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+               x = readw(ahd->bshs[0].maddr + port);
+       } else {
+               x = inw(ahd->bshs[(port) >> 8].ioport + ((port) & 0xFF));
+       }
+#else
+       x = inw(ahd->bshs[(port) >> 8].ioport + ((port) & 0xFF));
+#endif
+       mb();
+       return (x);
+}
+
+static __inline void
+ahd_outb(struct ahd_softc * ahd, long port, uint8_t val)
+{
+#ifdef MMAPIO
+       if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+               writeb(val, ahd->bshs[0].maddr + port);
+       } else {
+               outb(val, ahd->bshs[(port) >> 8].ioport + (port & 0xFF));
+       }
+#else
+       outb(val, ahd->bshs[(port) >> 8].ioport + (port & 0xFF));
+#endif
+       mb();
+}
+
+static __inline void
+ahd_outw_atomic(struct ahd_softc * ahd, long port, uint16_t val)
+{
+#ifdef MMAPIO
+       if (ahd->tags[0] == BUS_SPACE_MEMIO) {
+               writew(val, ahd->bshs[0].maddr + port);
+       } else {
+               outw(val, ahd->bshs[(port) >> 8].ioport + (port & 0xFF));
+       }
+#else
+       outw(val, ahd->bshs[(port) >> 8].ioport + (port & 0xFF));
+#endif
+       mb();
+}
+
+static __inline void
+ahd_outsb(struct ahd_softc * ahd, long port, uint8_t *array, int count)
+{
+       int i;
+
+       /*
+        * There is probably a more efficient way to do this on Linux
+        * but we don't use this for anything speed critical and this
+        * should work.
+        */
+       for (i = 0; i < count; i++)
+               ahd_outb(ahd, port, *array++);
+}
+
+static __inline void
+ahd_insb(struct ahd_softc * ahd, long port, uint8_t *array, int count)
+{
+       int i;
+
+       /*
+        * There is probably a more efficient way to do this on Linux
+        * but we don't use this for anything speed critical and this
+        * should work.
+        */
+       for (i = 0; i < count; i++)
+               *array++ = ahd_inb(ahd, port);
+}
+
+/**************************** Initialization **********************************/
+int            ahd_linux_register_host(struct ahd_softc *,
+                                       Scsi_Host_Template *);
+
+uint64_t       ahd_linux_get_memsize(void);
+
+/*************************** Pretty Printing **********************************/
+struct info_str {
+       char *buffer;
+       int length;
+       off_t offset;
+       int pos;
+};
+
+void   ahd_format_transinfo(struct info_str *info,
+                            struct ahd_transinfo *tinfo);
+
+/******************************** Locking *************************************/
+/* Lock protecting internal data structures */
+static __inline void ahd_lockinit(struct ahd_softc *);
+static __inline void ahd_lock(struct ahd_softc *, unsigned long *flags);
+static __inline void ahd_unlock(struct ahd_softc *, unsigned long *flags);
+
+/* Lock held during command compeletion to the upper layer */
+static __inline void ahd_done_lockinit(struct ahd_softc *);
+static __inline void ahd_done_lock(struct ahd_softc *, unsigned long *flags);
+static __inline void ahd_done_unlock(struct ahd_softc *, unsigned long *flags);
+
+/* Lock held during ahd_list manipulation and ahd softc frees */
+extern spinlock_t ahd_list_spinlock;
+static __inline void ahd_list_lockinit(void);
+static __inline void ahd_list_lock(unsigned long *flags);
+static __inline void ahd_list_unlock(unsigned long *flags);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93)
+static __inline void
+ahd_lockinit(struct ahd_softc *ahd)
+{
+       spin_lock_init(&ahd->platform_data->spin_lock);
+}
+
+static __inline void
+ahd_lock(struct ahd_softc *ahd, unsigned long *flags)
+{
+       *flags = 0;
+       spin_lock_irqsave(&ahd->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahd_unlock(struct ahd_softc *ahd, unsigned long *flags)
+{
+       spin_unlock_irqrestore(&ahd->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahd_done_lockinit(struct ahd_softc *ahd)
+{
+       /* We don't own the iorequest lock, so we don't initialize it. */
+}
+
+static __inline void
+ahd_done_lock(struct ahd_softc *ahd, unsigned long *flags)
+{
+       *flags = 0;
+       spin_lock_irqsave(&io_request_lock, *flags);
+}
+
+static __inline void
+ahd_done_unlock(struct ahd_softc *ahd, unsigned long *flags)
+{
+       spin_unlock_irqrestore(&io_request_lock, *flags);
+}
+
+static __inline void
+ahd_list_lockinit()
+{
+       spin_lock_init(&ahd_list_spinlock);
+}
+
+static __inline void
+ahd_list_lock(unsigned long *flags)
+{
+       *flags = 0;
+       spin_lock_irqsave(&ahd_list_spinlock, *flags);
+}
+
+static __inline void
+ahd_list_unlock(unsigned long *flags)
+{
+       spin_unlock_irqrestore(&ahd_list_spinlock, *flags);
+}
+
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) */
+
+ahd_lockinit(struct ahd_softc *ahd)
+{
+}
+
+static __inline void
+ahd_lock(struct ahd_softc *ahd, unsigned long *flags)
+{
+       *flags = 0;
+       save_flags(*flags);
+       cli();
+}
+
+static __inline void
+ahd_unlock(struct ahd_softc *ahd, unsigned long *flags)
+{
+       restore_flags(*flags);
+}
+
+ahd_done_lockinit(struct ahd_softc *ahd)
+{
+}
+
+static __inline void
+ahd_done_lock(struct ahd_softc *ahd, unsigned long *flags)
+{
+       /*
+        * The done lock is always held while
+        * the ahd lock is held so blocking
+        * interrupts again would have no effect.
+        */
+}
+
+static __inline void
+ahd_done_unlock(struct ahd_softc *ahd, unsigned long *flags)
+{
+}
+
+static __inline void
+ahd_list_lockinit()
+{
+}
+
+static __inline void
+ahd_list_lock(unsigned long *flags)
+{
+       *flags = 0;
+       save_flags(*flags);
+       cli();
+}
+
+static __inline void
+ahd_list_unlock(unsigned long *flags)
+{
+       restore_flags(*flags);
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) */
+
+/******************************* PCI Definitions ******************************/
+/*
+ * PCIM_xxx: mask to locate subfield in register
+ * PCIR_xxx: config register offset
+ * PCIC_xxx: device class
+ * PCIS_xxx: device subclass
+ * PCIP_xxx: device programming interface
+ * PCIV_xxx: PCI vendor ID (only required to fixup ancient devices)
+ * PCID_xxx: device ID
+ */
+#define PCIR_DEVVENDOR         0x00
+#define PCIR_VENDOR            0x00
+#define PCIR_DEVICE            0x02
+#define PCIR_COMMAND           0x04
+#define PCIM_CMD_PORTEN                0x0001
+#define PCIM_CMD_MEMEN         0x0002
+#define PCIM_CMD_BUSMASTEREN   0x0004
+#define PCIM_CMD_MWRICEN       0x0010
+#define PCIM_CMD_PERRESPEN     0x0040
+#define        PCIM_CMD_SERRESPEN      0x0100
+#define PCIR_STATUS            0x06
+#define PCIR_REVID             0x08
+#define PCIR_PROGIF            0x09
+#define PCIR_SUBCLASS          0x0a
+#define PCIR_CLASS             0x0b
+#define PCIR_CACHELNSZ         0x0c
+#define PCIR_LATTIMER          0x0d
+#define PCIR_HEADERTYPE                0x0e
+#define PCIM_MFDEV             0x80
+#define PCIR_BIST              0x0f
+#define PCIR_CAP_PTR           0x34
+
+/* config registers for header type 0 devices */
+#define PCIR_MAPS      0x10
+#define PCIR_SUBVEND_0 0x2c
+#define PCIR_SUBDEV_0  0x2e
+
+/****************************** PCI-X definitions *****************************/
+#define PCIXR_COMMAND  0x96
+#define PCIXR_DEVADDR  0x98
+#define PCIXM_DEVADDR_FNUM     0x0003  /* Function Number */
+#define PCIXM_DEVADDR_DNUM     0x00F8  /* Device Number */
+#define PCIXM_DEVADDR_BNUM     0xFF00  /* Bus Number */
+#define PCIXR_STATUS   0x9A
+#define PCIXM_STATUS_64BIT     0x0001  /* Active 64bit connection to device. */
+#define PCIXM_STATUS_133CAP    0x0002  /* Device is 133MHz capable */
+#define PCIXM_STATUS_SCDISC    0x0004  /* Split Completion Discarded */
+#define PCIXM_STATUS_UNEXPSC   0x0008  /* Unexpected Split Completion */
+#define PCIXM_STATUS_CMPLEXDEV 0x0010  /* Device Complexity (set == bridge) */
+#define PCIXM_STATUS_MAXMRDBC  0x0060  /* Maximum Burst Read Count */
+#define PCIXM_STATUS_MAXSPLITS 0x0380  /* Maximum Split Transactions */
+#define PCIXM_STATUS_MAXCRDS   0x1C00  /* Maximum Cumulative Read Size */
+#define PCIXM_STATUS_RCVDSCEM  0x2000  /* Received a Split Comp w/Error msg */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+extern struct pci_driver aic79xx_pci_driver;
+#endif
+
+typedef enum
+{
+       AHD_POWER_STATE_D0,
+       AHD_POWER_STATE_D1,
+       AHD_POWER_STATE_D2,
+       AHD_POWER_STATE_D3
+} ahd_power_state;
+
+void ahd_power_state_change(struct ahd_softc *ahd,
+                           ahd_power_state new_state);
+
+/******************************* PCI Routines *********************************/
+/*
+ * We need to use the bios32.h routines if we are kernel version 2.1.92 or less.
+ */
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,1,92)
+#if defined(__sparc_v9__) || defined(__powerpc__)
+#error "PPC and Sparc platforms are only support under 2.1.92 and above"
+#endif
+#include <linux/bios32.h>
+#endif
+
+int                     ahd_linux_pci_probe(Scsi_Host_Template *);
+int                     ahd_pci_map_registers(struct ahd_softc *ahd);
+int                     ahd_pci_map_int(struct ahd_softc *ahd);
+
+static __inline uint32_t ahd_pci_read_config(ahd_dev_softc_t pci,
+                                            int reg, int width);
+
+static __inline uint32_t
+ahd_pci_read_config(ahd_dev_softc_t pci, int reg, int width)
+{
+       switch (width) {
+       case 1:
+       {
+               uint8_t retval;
+
+               pci_read_config_byte(pci, reg, &retval);
+               return (retval);
+       }
+       case 2:
+       {
+               uint16_t retval;
+               pci_read_config_word(pci, reg, &retval);
+               return (retval);
+       }
+       case 4:
+       {
+               uint32_t retval;
+               pci_read_config_dword(pci, reg, &retval);
+               return (retval);
+       }
+       default:
+               panic("ahd_pci_read_config: Read size too big");
+               /* NOTREACHED */
+               return (0);
+       }
+}
+
+static __inline void ahd_pci_write_config(ahd_dev_softc_t pci,
+                                         int reg, uint32_t value,
+                                         int width);
+
+static __inline void
+ahd_pci_write_config(ahd_dev_softc_t pci, int reg, uint32_t value, int width)
+{
+       switch (width) {
+       case 1:
+               pci_write_config_byte(pci, reg, value);
+               break;
+       case 2:
+               pci_write_config_word(pci, reg, value);
+               break;
+       case 4:
+               pci_write_config_dword(pci, reg, value);
+               break;
+       default:
+               panic("ahd_pci_write_config: Write size too big");
+               /* NOTREACHED */
+       }
+}
+
+static __inline int ahd_get_pci_function(ahd_dev_softc_t);
+static __inline int
+ahd_get_pci_function(ahd_dev_softc_t pci)
+{
+       return (PCI_FUNC(pci->devfn));
+}
+
+static __inline int ahd_get_pci_slot(ahd_dev_softc_t);
+static __inline int
+ahd_get_pci_slot(ahd_dev_softc_t pci)
+{
+       return (PCI_SLOT(pci->devfn));
+}
+
+static __inline int ahd_get_pci_bus(ahd_dev_softc_t);
+static __inline int
+ahd_get_pci_bus(ahd_dev_softc_t pci)
+{
+       return (pci->bus->number);
+}
+
+static __inline void ahd_flush_device_writes(struct ahd_softc *);
+static __inline void
+ahd_flush_device_writes(struct ahd_softc *ahd)
+{
+       /* XXX Is this sufficient for all architectures??? */
+       ahd_inb(ahd, INTSTAT);
+}
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,0)
+#define pci_map_sg(pdev, sg_list, nseg, direction) (nseg)
+#define pci_unmap_sg(pdev, sg_list, nseg, direction)
+#define sg_dma_address(sg) (VIRT_TO_BUS((sg)->address))
+#define sg_dma_len(sg) ((sg)->length)
+#define pci_map_single(pdev, buffer, bufflen, direction) \
+       (VIRT_TO_BUS(buffer))
+#define pci_unmap_single(pdev, buffer, buflen, direction)
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
+#define ahd_pci_set_dma_mask pci_set_dma_mask
+#else
+/*
+ * Always "return" 0 for success.
+ */
+#define ahd_pci_set_dma_mask(dev_softc, mask)  \
+       (((dev_softc)->dma_mask = mask) && 0)
+#endif
+/*********************** Transaction Access Wrappers **************************/
+static __inline void ahd_set_transaction_status(struct scb *, uint32_t);
+static __inline
+void ahd_set_transaction_status(struct scb *scb, uint32_t status)
+{
+       scb->io_ctx->result &= ~(CAM_STATUS_MASK << 16);
+       scb->io_ctx->result |= status << 16;
+}
+
+static __inline void ahd_set_scsi_status(struct scb *, uint32_t);
+static __inline
+void ahd_set_scsi_status(struct scb *scb, uint32_t status)
+{
+       scb->io_ctx->result &= ~0xFFFF;
+       scb->io_ctx->result |= status;
+}
+
+static __inline uint32_t ahd_get_transaction_status(struct scb *);
+static __inline
+uint32_t ahd_get_transaction_status(struct scb *scb)
+{
+       return ((scb->io_ctx->result >> 16) & CAM_STATUS_MASK);
+}
+
+static __inline uint32_t ahd_get_scsi_status(struct scb *);
+static __inline
+uint32_t ahd_get_scsi_status(struct scb *scb)
+{
+       return (scb->io_ctx->result & 0xFFFF);
+}
+
+static __inline void ahd_set_transaction_tag(struct scb *, int, u_int);
+static __inline
+void ahd_set_transaction_tag(struct scb *scb, int enabled, u_int type)
+{
+       /*
+        * Nothing to do for linux as the incoming transaction
+        * has no concept of tag/non tagged, etc.
+        */
+}
+
+static __inline u_long ahd_get_transfer_length(struct scb *);
+static __inline
+u_long ahd_get_transfer_length(struct scb *scb)
+{
+       return (scb->platform_data->xfer_len);
+}
+
+static __inline int ahd_get_transfer_dir(struct scb *);
+static __inline
+int ahd_get_transfer_dir(struct scb *scb)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,40)
+       return (scb->io_ctx->sc_data_direction);
+#else
+       if (scb->io_ctx->bufflen == 0)
+               return (CAM_DIR_NONE);
+
+       switch(scb->io_ctx->cmnd[0]) {
+       case 0x08:  /* READ(6)  */
+       case 0x28:  /* READ(10) */
+       case 0xA8:  /* READ(12) */
+               return (CAM_DIR_IN);
+        case 0x0A:  /* WRITE(6)  */
+        case 0x2A:  /* WRITE(10) */
+        case 0xAA:  /* WRITE(12) */
+               return (CAM_DIR_OUT);
+        default:
+               return (CAM_DIR_NONE);
+        }
+#endif
+}
+
+static __inline void ahd_set_residual(struct scb *, u_long);
+static __inline
+void ahd_set_residual(struct scb *scb, u_long resid)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       scb->io_ctx->resid = resid;
+#else
+       scb->platform_data->resid = resid;
+#endif
+}
+
+static __inline void ahd_set_sense_residual(struct scb *, u_long);
+static __inline
+void ahd_set_sense_residual(struct scb *scb, u_long resid)
+{
+       /* This can't be reported in Linux */
+}
+
+static __inline u_long ahd_get_residual(struct scb *);
+static __inline
+u_long ahd_get_residual(struct scb *scb)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       return (scb->io_ctx->resid);
+#else
+       return (scb->platform_data->resid);
+#endif
+}
+
+static __inline int ahd_perform_autosense(struct scb *);
+static __inline
+int ahd_perform_autosense(struct scb *scb)
+{
+       /*
+        * We always perform autosense in Linux.
+        * On other platforms this is set on a
+        * per-transaction basis.
+        */
+       return (1);
+}
+
+static __inline uint32_t
+ahd_get_sense_bufsize(struct ahd_softc *ahd, struct scb *scb)
+{
+       return (sizeof(struct scsi_sense_data));
+}
+
+static __inline void ahd_notify_xfer_settings_change(struct ahd_softc *,
+                                                    struct ahd_devinfo *);
+static __inline void
+ahd_notify_xfer_settings_change(struct ahd_softc *ahd,
+                               struct ahd_devinfo *devinfo)
+{
+       /* Nothing to do here for linux */
+}
+
+static __inline void ahd_platform_scb_free(struct ahd_softc *ahd,
+                                          struct scb *scb);
+static __inline void
+ahd_platform_scb_free(struct ahd_softc *ahd, struct scb *scb)
+{
+       ahd->flags &= ~AHD_RESOURCE_SHORTAGE;
+}
+
+int    ahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg);
+void   ahd_platform_free(struct ahd_softc *ahd);
+void   ahd_platform_freeze_devq(struct ahd_softc *ahd, struct scb *scb);
+void   ahd_freeze_simq(struct ahd_softc *ahd);
+void   ahd_release_simq(struct ahd_softc *ahd);
+static __inline void   ahd_freeze_scb(struct scb *scb);
+static __inline void
+ahd_freeze_scb(struct scb *scb)
+{
+       /* Noting to do here for linux */
+}
+
+void   ahd_platform_set_tags(struct ahd_softc *ahd,
+                             struct ahd_devinfo *devinfo, ahd_queue_alg);
+int    ahd_platform_abort_scbs(struct ahd_softc *ahd, int target,
+                               char channel, int lun, u_int tag,
+                               role_t role, uint32_t status);
+void   ahd_linux_isr(int irq, void *dev_id, struct pt_regs * regs);
+void   ahd_platform_flushwork(struct ahd_softc *ahd);
+int    ahd_softc_comp(struct ahd_softc *, struct ahd_softc *);
+void   ahd_done(struct ahd_softc*, struct scb*);
+void   ahd_send_async(struct ahd_softc *, char channel,
+                      u_int target, u_int lun, ac_code, void *);
+void   ahd_print_path(struct ahd_softc *, struct scb *);
+void   ahd_platform_dump_card_state(struct ahd_softc *ahd);
+
+#ifdef CONFIG_PCI
+#define AHD_PCI_CONFIG 1
+#else
+#define AHD_PCI_CONFIG 0
+#endif
+#define bootverbose aic79xx_verbose
+extern int aic79xx_verbose;
+#endif /* _AIC79XX_LINUX_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_osm_pci.c b/xen/drivers/scsi/aic7xxx/aic79xx_osm_pci.c
new file mode 100644 (file)
index 0000000..53cf44e
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * Linux driver attachment glue for PCI based U320 controllers.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm_pci.c#11 $
+ */
+
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+struct pci_device_id
+{
+};
+#endif
+
+static int     ahd_linux_pci_dev_probe(struct pci_dev *pdev,
+                                       const struct pci_device_id *ent);
+static int     ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd,
+                                                u_long *base, u_long *base2);
+static int     ahd_linux_pci_reserve_mem_region(struct ahd_softc *ahd,
+                                                u_long *bus_addr,
+                                                uint8_t **maddr);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+static void    ahd_linux_pci_dev_remove(struct pci_dev *pdev);
+
+/* We do our own ID filtering.  So, grab all SCSI storage class devices. */
+static struct pci_device_id ahd_linux_pci_id_table[] = {
+       {
+               0x9005, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+               PCI_CLASS_STORAGE_SCSI << 8, 0xFFFF00, 0
+       },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, ahd_linux_pci_id_table);
+
+struct pci_driver aic79xx_pci_driver = {
+       name:           "aic79xx",
+       probe:          ahd_linux_pci_dev_probe,
+       remove:         ahd_linux_pci_dev_remove,
+       id_table:       ahd_linux_pci_id_table
+};
+
+static void
+ahd_linux_pci_dev_remove(struct pci_dev *pdev)
+{
+       struct ahd_softc *ahd;
+       u_long l;
+
+       /*
+        * We should be able to just perform
+        * the free directly, but check our
+        * list for extra sanity.
+        */
+       ahd_list_lock(&l);
+       ahd = ahd_find_softc((struct ahd_softc *)pdev->driver_data);
+       if (ahd != NULL) {
+               u_long s;
+
+               ahd_lock(ahd, &s);
+               ahd_intr_enable(ahd, FALSE);
+               ahd_unlock(ahd, &s);
+               ahd_free(ahd);
+       }
+       ahd_list_unlock(&l);
+}
+#endif /* !LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) */
+
+static int
+ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       char             buf[80];
+       struct           ahd_softc *ahd;
+       ahd_dev_softc_t  pci;
+       struct           ahd_pci_identity *entry;
+       char            *name;
+       int              error;
+
+       /*
+        * Some BIOSen report the same device multiple times.
+        */
+       TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+               struct pci_dev *probed_pdev;
+
+               probed_pdev = ahd->dev_softc;
+               if (probed_pdev->bus->number == pdev->bus->number
+                && probed_pdev->devfn == pdev->devfn)
+                       break;
+       }
+       if (ahd != NULL) {
+               /* Skip duplicate. */
+               return (-ENODEV);
+       }
+
+       pci = pdev;
+       entry = ahd_find_pci_device(pci);
+       if (entry == NULL)
+               return (-ENODEV);
+
+       /*
+        * Allocate a softc for this card and
+        * set it up for attachment by our
+        * common detect routine.
+        */
+       sprintf(buf, "ahd_pci:%d:%d:%d",
+               ahd_get_pci_bus(pci),
+               ahd_get_pci_slot(pci),
+               ahd_get_pci_function(pci));
+       name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+       if (name == NULL)
+               return (-ENOMEM);
+       strcpy(name, buf);
+       ahd = ahd_alloc(NULL, name);
+       if (ahd == NULL)
+               return (-ENOMEM);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+       if (pci_enable_device(pdev)) {
+               ahd_free(ahd);
+               return (-ENODEV);
+       }
+       pci_set_master(pdev);
+
+       if (sizeof(bus_addr_t) > 4) {
+               uint64_t memsize;
+
+               memsize = ahd_linux_get_memsize();
+               if (memsize >= 0x8000000000
+                && ahd_pci_set_dma_mask(pdev, 0xFFFFFFFFFFFFFFFFULL) == 0) {
+                       ahd->flags |= AHD_64BIT_ADDRESSING;
+                       ahd->platform_data->hw_dma_mask =
+                           (bus_addr_t)(0xFFFFFFFFFFFFFFFFULL&(bus_addr_t)~0);
+               } else if (memsize > 0x80000000
+                       && ahd_pci_set_dma_mask(pdev, 0x7FFFFFFFFFULL) == 0) {
+                       ahd->flags |= AHD_39BIT_ADDRESSING;
+                       ahd->platform_data->hw_dma_mask =
+                           (bus_addr_t)(0x7FFFFFFFFFULL & (bus_addr_t)~0);
+               }
+       }
+#endif
+       ahd->dev_softc = pci;
+       error = ahd_pci_config(ahd, entry);
+       if (error != 0) {
+               ahd_free(ahd);
+               return (-error);
+       }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+       pdev->driver_data = ahd;
+       if (aic79xx_detect_complete)
+               ahd_linux_register_host(ahd, aic79xx_driver_template);
+#endif
+       return (0);
+}
+
+int
+ahd_linux_pci_probe(Scsi_Host_Template *template)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+       return (pci_module_init(&aic79xx_pci_driver));
+#else
+       struct pci_dev *pdev;
+       u_int class;
+       int found;
+
+       /* If we don't have a PCI bus, we can't find any adapters. */
+       if (pci_present() == 0)
+               return (0);
+
+       found = 0;
+       pdev = NULL;
+       class = PCI_CLASS_STORAGE_SCSI << 8;
+       while ((pdev = pci_find_class(class, pdev)) != NULL) {
+               ahd_dev_softc_t pci;
+               int error;
+
+               pci = pdev;
+               error = ahd_linux_pci_dev_probe(pdev, /*pci_devid*/NULL);
+               if (error == 0)
+                       found++;
+       }
+       return (found);
+#endif
+}
+
+static int
+ahd_linux_pci_reserve_io_regions(struct ahd_softc *ahd, u_long *base,
+                                u_long *base2)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       *base = pci_resource_start(ahd->dev_softc, 0);
+       /*
+        * This is really the 3rd bar and should be at index 2,
+        * but the Linux PCI code doesn't know how to "count" 64bit
+        * bars.
+        */
+       *base2 = pci_resource_start(ahd->dev_softc, 3);
+#else
+       *base = ahd_pci_read_config(ahd->dev_softc, AHD_PCI_IOADDR0, 4);
+       *base2 = ahd_pci_read_config(ahd->dev_softc, AHD_PCI_IOADDR1, 4);
+       *base &= PCI_BASE_ADDRESS_IO_MASK;
+       *base2 &= PCI_BASE_ADDRESS_IO_MASK;
+#endif
+       if (*base == 0 || *base2 == 0)
+               return (ENOMEM);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+       if (check_region(*base, 256) != 0
+        || check_region(*base2, 256) != 0)
+               return (ENOMEM);
+       else {
+               request_region(*base, 256, "aic79xx");
+               request_region(*base2, 256, "aic79xx");
+       }
+#else
+       if (request_region(*base, 256, "aic79xx") == 0)
+               return (ENOMEM);
+       if (request_region(*base2, 256, "aic79xx") == 0) {
+               release_region(*base2, 256);
+               return (ENOMEM);
+       }
+#endif
+       return (0);
+}
+
+static int
+ahd_linux_pci_reserve_mem_region(struct ahd_softc *ahd,
+                                u_long *bus_addr,
+                                uint8_t **maddr)
+{
+       u_long  start;
+       u_long  base_page;
+       u_long  base_offset;
+       int     error;
+
+       if ((ahd->chip & AHD_BUS_MASK) == AHD_PCIX
+        && (ahd->bugs & AHD_PCIX_MMAPIO_BUG) != 0)
+               return (ENOMEM);
+
+       error = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       start = pci_resource_start(ahd->dev_softc, 1);
+       base_page = start & PAGE_MASK;
+       base_offset = start - base_page;
+#else
+       start = ahd_pci_read_config(ahd->dev_softc, PCIR_MAPS+4, 4);
+       base_offset = start & PCI_BASE_ADDRESS_MEM_MASK;
+       base_page = base_offset & PAGE_MASK;
+       base_offset -= base_page;
+#endif
+       if (start != 0) {
+               *bus_addr = start;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+               if (request_mem_region(start, 0x1000, "aic79xx") == 0)
+                       error = ENOMEM;
+#endif
+               if (error == 0) {
+                       *maddr = ioremap_nocache(base_page, base_offset + 256);
+                       if (*maddr == NULL) {
+                               error = ENOMEM;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+                               release_mem_region(start, 0x1000);
+#endif
+                       } else
+                               *maddr += base_offset;
+               }
+       } else
+               error = ENOMEM;
+       return (error);
+}
+
+int
+ahd_pci_map_registers(struct ahd_softc *ahd)
+{
+       uint32_t command;
+       u_long   base;
+       uint8_t *maddr;
+       int      error;
+
+       /*
+        * If its allowed, we prefer memory mapped access.
+        */
+       command = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, 4);
+       command &= ~(PCIM_CMD_PORTEN|PCIM_CMD_MEMEN);
+       base = 0;
+       maddr = NULL;
+#ifdef MMAPIO
+       error = ahd_linux_pci_reserve_mem_region(ahd, &base, &maddr);
+       if (error == 0) {
+               ahd->platform_data->mem_busaddr = base;
+               ahd->tags[0] = BUS_SPACE_MEMIO;
+               ahd->bshs[0].maddr = maddr;
+               ahd->tags[1] = BUS_SPACE_MEMIO;
+               ahd->bshs[1].maddr = maddr + 0x100;
+               ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND,
+                                    command | PCIM_CMD_MEMEN, 4);
+
+               /*
+                * Do a quick test to see if memory mapped
+                * I/O is functioning correctly.
+                */
+               if (ahd_inb(ahd, HCNTRL) == 0xFF) {
+
+                       printf("aic79xx: PCI Device %d:%d:%d "
+                              "failed memory mapped test\n",
+                              ahd_get_pci_bus(ahd->dev_softc),
+                              ahd_get_pci_slot(ahd->dev_softc),
+                              ahd_get_pci_function(ahd->dev_softc));
+                       ahd->bshs[0].maddr = NULL;
+                       maddr = NULL;
+               } else
+                       command |= PCIM_CMD_MEMEN;
+       } else {
+               printf("aic79xx: PCI%d:%d:%d MEM region 0x%lx "
+                      "unavailable. Cannot memory map device.\n",
+                      ahd_get_pci_bus(ahd->dev_softc),
+                      ahd_get_pci_slot(ahd->dev_softc),
+                      ahd_get_pci_function(ahd->dev_softc),
+                      base);
+       }
+#endif
+
+       if (maddr == NULL) {
+               u_long   base2;
+
+               error = ahd_linux_pci_reserve_io_regions(ahd, &base, &base2);
+               if (error == 0) {
+                       ahd->tags[0] = BUS_SPACE_PIO;
+                       ahd->tags[1] = BUS_SPACE_PIO;
+                       ahd->bshs[0].ioport = base;
+                       ahd->bshs[1].ioport = base2;
+                       command |= PCIM_CMD_PORTEN;
+               } else {
+                       printf("aic79xx: PCI%d:%d:%d IO regions 0x%lx and 0x%lx"
+                              "unavailable. Cannot map device.\n",
+                              ahd_get_pci_bus(ahd->dev_softc),
+                              ahd_get_pci_slot(ahd->dev_softc),
+                              ahd_get_pci_function(ahd->dev_softc),
+                              base, base2);
+               }
+       }
+       ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, command, 4);
+       return (error);
+}
+
+int
+ahd_pci_map_int(struct ahd_softc *ahd)
+{
+       int error;
+
+       error = request_irq(ahd->dev_softc->irq, ahd_linux_isr,
+                           SA_SHIRQ, "aic79xx", ahd);
+       if (error == 0)
+               ahd->platform_data->irq = ahd->dev_softc->irq;
+       
+       return (-error);
+}
+
+void
+ahd_power_state_change(struct ahd_softc *ahd, ahd_power_state new_state)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+       pci_set_power_state(ahd->dev_softc, new_state);
+#else
+       uint32_t cap;
+       u_int cap_offset;
+
+       /*
+        * Traverse the capability list looking for
+        * the power management capability.
+        */
+       cap = 0;
+       cap_offset = ahd_pci_read_config(ahd->dev_softc,
+                                        PCIR_CAP_PTR, /*bytes*/1);
+       while (cap_offset != 0) {
+
+               cap = ahd_pci_read_config(ahd->dev_softc,
+                                         cap_offset, /*bytes*/4);
+               if ((cap & 0xFF) == 1
+                && ((cap >> 16) & 0x3) > 0) {
+                       uint32_t pm_control;
+
+                       pm_control = ahd_pci_read_config(ahd->dev_softc,
+                                                        cap_offset + 4,
+                                                        /*bytes*/4);
+                       pm_control &= ~0x3;
+                       pm_control |= new_state;
+                       ahd_pci_write_config(ahd->dev_softc,
+                                            cap_offset + 4,
+                                            pm_control, /*bytes*/2);
+                       break;
+               }
+               cap_offset = (cap >> 8) & 0xFF;
+       }
+#endif 
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_pci.c b/xen/drivers/scsi/aic7xxx/aic79xx_pci.c
new file mode 100644 (file)
index 0000000..b7bd3cc
--- /dev/null
@@ -0,0 +1,792 @@
+/*
+ * Product specific probe and attach routines for:
+ *     aic7901 and aic7902 SCSI controllers
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx_pci.c#32 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+#else
+#include <dev/aic7xxx/aic79xx_osm.h>
+#include <dev/aic7xxx/aic79xx_inline.h>
+#endif
+
+static __inline uint64_t
+ahd_compose_id(u_int device, u_int vendor, u_int subdevice, u_int subvendor)
+{
+       uint64_t id;
+
+       id = subvendor
+          | (subdevice << 16)
+          | ((uint64_t)vendor << 32)
+          | ((uint64_t)device << 48);
+
+       return (id);
+}
+
+#define ID_ALL_MASK                    0xFFFFFFFFFFFFFFFFull
+#define ID_DEV_VENDOR_MASK             0xFFFFFFFF00000000ull
+#define ID_9005_GENERIC_MASK           0xFFF0FFFF00000000ull
+
+#define ID_AIC7901                     0x800F9005FFFF9005ull
+#define ID_AIC7901_IROC                        0x80089005FFFF9005ull
+#define ID_AHA_29320                   0x8000900500609005ull
+#define ID_AHA_29320LP                 0x8000900500409005ull
+
+#define ID_AIC7902                     0x801F9005FFFF9005ull
+#define ID_AIC7902_IROC                        0x80189005FFFF9005ull
+#define ID_AHA_39320                   0x8010900500409005ull
+#define ID_AHA_39320D                  0x8011900500419005ull
+#define ID_AHA_39320D_CPQ              0x8011900500AC0E11ull
+#define ID_AIC7902_PCI_REV_A3          0x2
+#define ID_AIC7902_PCI_REV_A4          0x3
+#define ID_AIC7902_PCI_REV_B0          0xFF    /* Rev Id not yet known. */
+#define SUBID_CPQ                      0x0E11
+
+#define DEVID_9005_TYPE(id) ((id) & 0xF)
+#define                DEVID_9005_TYPE_HBA             0x0     /* Standard Card */
+#define                DEVID_9005_TYPE_HBA_2EXT        0x1     /* 2 External Ports */
+#define                DEVID_9005_TYPE_IROC            0x8     /* Raid(0,1,10) Card */
+#define                DEVID_9005_TYPE_MB              0xF     /* On Motherboard */
+
+#define DEVID_9005_MFUNC(id) ((id) & 0x10)
+
+#define DEVID_9005_PACKETIZED(id) ((id) & 0x8000)
+
+#define SUBID_9005_TYPE(id) ((id) & 0xF)
+#define                SUBID_9005_TYPE_HBA             0x0     /* Standard Card */
+#define                SUBID_9005_TYPE_MB              0xF     /* On Motherboard */
+
+#define SUBID_9005_AUTOTERM(id)        (((id) & 0x10) == 0)
+
+#define SUBID_9005_LEGACYCONN_FUNC(id) ((id) & 0x20)
+
+#define SUBID_9005_SEEPTYPE(id) ((id) & 0x0C0) >> 6)
+#define                SUBID_9005_SEEPTYPE_NONE        0x0
+#define                SUBID_9005_SEEPTYPE_4K          0x1
+
+static ahd_device_setup_t ahd_aic7901_setup;
+static ahd_device_setup_t ahd_aic7902_setup;
+
+struct ahd_pci_identity ahd_pci_ident_table [] =
+{
+       /* aic7901 based controllers */
+       {
+               ID_AHA_29320,
+               ID_ALL_MASK,
+               "Adaptec 29320 Ultra320 SCSI adapter",
+               ahd_aic7901_setup
+       },
+       {
+               ID_AHA_29320LP,
+               ID_ALL_MASK,
+               "Adaptec 29320LP Ultra320 SCSI adapter",
+               ahd_aic7901_setup
+       },
+       /* aic7902 based controllers */ 
+       {
+               ID_AHA_39320,
+               ID_ALL_MASK,
+               "Adaptec 39320 Ultra320 SCSI adapter",
+               ahd_aic7902_setup
+       },
+       {
+               ID_AHA_39320D,
+               ID_ALL_MASK,
+               "Adaptec 39320D Ultra320 SCSI adapter",
+               ahd_aic7902_setup
+       },
+       {
+               ID_AHA_39320D_CPQ,
+               ID_ALL_MASK,
+               "Adaptec (Compaq OEM) 39320D Ultra320 SCSI adapter",
+               ahd_aic7902_setup
+       },
+       /* Generic chip probes for devices we don't know 'exactly' */
+       {
+               ID_AIC7901 & ID_9005_GENERIC_MASK,
+               ID_9005_GENERIC_MASK,
+               "Adaptec aic7901 Ultra320 SCSI adapter",
+               ahd_aic7901_setup
+       },
+       {
+               ID_AIC7902 & ID_9005_GENERIC_MASK,
+               ID_9005_GENERIC_MASK,
+               "Adaptec aic7902 Ultra320 SCSI adapter",
+               ahd_aic7902_setup
+       }
+};
+
+const u_int ahd_num_pci_devs = NUM_ELEMENTS(ahd_pci_ident_table);
+               
+#define        DEVCONFIG               0x40
+#define                PCIXINITPAT     0x0000E000ul
+#define                        PCIXINIT_PCI33_66       0x0000E000ul
+#define                        PCIXINIT_PCIX50_66      0x0000C000ul
+#define                        PCIXINIT_PCIX66_100     0x0000A000ul
+#define                        PCIXINIT_PCIX100_133    0x00008000ul
+#define        PCI_BUS_MODES_INDEX(devconfig)  \
+       (((devconfig) & PCIXINITPAT) >> 13)
+static const char *pci_bus_modes[] =
+{
+       "PCI bus mode unknown",
+       "PCI bus mode unknown",
+       "PCI bus mode unknown",
+       "PCI bus mode unknown",
+       "PCI-X 101-133Mhz",
+       "PCI-X 67-100Mhz",
+       "PCI-X 50-66Mhz",
+       "PCI 33 or 66Mhz"
+};
+
+#define                TESTMODE        0x00000800ul
+#define                IRDY_RST        0x00000200ul
+#define                FRAME_RST       0x00000100ul
+#define                PCI64BIT        0x00000080ul
+#define                MRDCEN          0x00000040ul
+#define                ENDIANSEL       0x00000020ul
+#define                MIXQWENDIANEN   0x00000008ul
+#define                DACEN           0x00000004ul
+#define                STPWLEVEL       0x00000002ul
+#define                QWENDIANSEL     0x00000001ul
+
+#define        DEVCONFIG1              0x44
+#define                PREQDIS         0x01
+
+#define        CSIZE_LATTIME           0x0c
+#define                CACHESIZE       0x000000fful
+#define                LATTIME         0x0000ff00ul
+
+static int     ahd_check_extport(struct ahd_softc *ahd);
+static void    ahd_configure_termination(struct ahd_softc *ahd,
+                                         u_int adapter_control);
+static void    ahd_pci_split_intr(struct ahd_softc *ahd, u_int intstat);
+
+struct ahd_pci_identity *
+ahd_find_pci_device(ahd_dev_softc_t pci)
+{
+       uint64_t  full_id;
+       uint16_t  device;
+       uint16_t  vendor;
+       uint16_t  subdevice;
+       uint16_t  subvendor;
+       struct    ahd_pci_identity *entry;
+       u_int     i;
+
+       vendor = ahd_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2);
+       device = ahd_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2);
+       subvendor = ahd_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2);
+       subdevice = ahd_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2);
+       full_id = ahd_compose_id(device,
+                                vendor,
+                                subdevice,
+                                subvendor);
+
+       for (i = 0; i < ahd_num_pci_devs; i++) {
+               entry = &ahd_pci_ident_table[i];
+               if (entry->full_id == (full_id & entry->id_mask)) {
+                       /* Honor exclusion entries. */
+                       if (entry->name == NULL)
+                               return (NULL);
+                       return (entry);
+               }
+       }
+       return (NULL);
+}
+
+int
+ahd_pci_config(struct ahd_softc *ahd, struct ahd_pci_identity *entry)
+{
+       struct scb_data *shared_scb_data;
+       u_long           l;
+       u_long           s;
+       u_int            command;
+       uint32_t         devconfig;
+       uint16_t         subvendor; 
+       int              error;
+
+       shared_scb_data = NULL;
+       error = entry->setup(ahd);
+       if (error != 0)
+               return (error);
+       
+       ahd->description = entry->name;
+       devconfig = ahd_pci_read_config(ahd->dev_softc, DEVCONFIG, /*bytes*/4);
+       if ((devconfig & PCIXINITPAT) == PCIXINIT_PCI33_66) {
+               ahd->chip |= AHD_PCI;
+               /* Disable PCIX workarounds when running in PCI mode. */
+               ahd->bugs &= ~AHD_PCIX_BUG_MASK;
+       } else {
+               ahd->chip |= AHD_PCIX;
+       }
+       ahd->bus_description = pci_bus_modes[PCI_BUS_MODES_INDEX(devconfig)];
+
+       /*
+        * Record if this is a Compaq board.
+        */
+       subvendor = ahd_pci_read_config(ahd->dev_softc,
+                                       PCIR_SUBVEND_0, /*bytes*/2);
+       if (subvendor == SUBID_CPQ)
+               ahd->flags |= AHD_CPQ_BOARD;
+
+       ahd_power_state_change(ahd, AHD_POWER_STATE_D0);
+
+       error = ahd_pci_map_registers(ahd);
+       if (error != 0)
+               return (error);
+
+       /*
+        * If we need to support high memory, enable dual
+        * address cycles.  This bit must be set to enable
+        * high address bit generation even if we are on a
+        * 64bit bus (PCI64BIT set in devconfig).
+        */
+       if ((ahd->flags & (AHD_39BIT_ADDRESSING|AHD_64BIT_ADDRESSING)) != 0) {
+               uint32_t devconfig;
+
+               if (bootverbose)
+                       printf("%s: Enabling 39Bit Addressing\n",
+                              ahd_name(ahd));
+               devconfig = ahd_pci_read_config(ahd->dev_softc,
+                                               DEVCONFIG, /*bytes*/4);
+               devconfig |= DACEN;
+               ahd_pci_write_config(ahd->dev_softc, DEVCONFIG,
+                                    devconfig, /*bytes*/4);
+       }
+       
+       /* Ensure busmastering is enabled */
+       command = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/1);
+       command |= PCIM_CMD_BUSMASTEREN;
+       ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, command, /*bytes*/1);
+
+       error = ahd_softc_init(ahd);
+       if (error != 0)
+               return (error);
+
+       ahd->bus_intr = ahd_pci_intr;
+
+       error = ahd_reset(ahd);
+       if (error != 0)
+               return (ENXIO);
+
+       ahd->pci_cachesize =
+           ahd_pci_read_config(ahd->dev_softc, CSIZE_LATTIME,
+                               /*bytes*/1) & CACHESIZE;
+       ahd->pci_cachesize *= 4;
+
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       /* See if we have a SEEPROM and perform auto-term */
+       error = ahd_check_extport(ahd);
+       if (error != 0)
+               return (error);
+
+       /* Core initialization */
+       error = ahd_init(ahd);
+       if (error != 0)
+               return (error);
+
+       /*
+        * Allow interrupts now that we are completely setup.
+        */
+       error = ahd_pci_map_int(ahd);
+       if (error != 0)
+               return (error);
+
+       ahd_list_lock(&l);
+       /*
+        * Link this softc in with all other ahd instances.
+        */
+       ahd_softc_insert(ahd);
+
+       ahd_lock(ahd, &s);
+       ahd_intr_enable(ahd, TRUE);
+       ahd_unlock(ahd, &s);
+
+       ahd_list_unlock(&l);
+       return (0);
+}
+
+/*
+ * Check the external port logic for a serial eeprom
+ * and termination/cable detection contrls.
+ */
+static int
+ahd_check_extport(struct ahd_softc *ahd)
+{
+       struct  seeprom_config *sc;
+       u_int   adapter_control;
+       int     have_seeprom;
+       int     error;
+
+       sc = ahd->seep_config;
+       have_seeprom = ahd_acquire_seeprom(ahd);
+       if (have_seeprom) {
+               u_int start_addr;
+
+               if (bootverbose) 
+                       printf("%s: Reading SEEPROM...", ahd_name(ahd));
+
+               /* Address is always in units of 16bit words */
+               start_addr = (sizeof(*sc) / 2) * (ahd->channel - 'A');
+
+               error = ahd_read_seeprom(ahd, (uint16_t *)sc,
+                                        start_addr, sizeof(*sc)/2);
+
+               if (error != 0) {
+                       printf("Unable to read SEEPROM\n");
+                       have_seeprom = 0;
+               } else {
+                       have_seeprom = ahd_verify_cksum(sc);
+
+                       if (bootverbose) {
+                               if (have_seeprom == 0)
+                                       printf ("checksum error\n");
+                               else
+                                       printf ("done.\n");
+                       }
+               }
+               ahd_release_seeprom(ahd);
+       }
+
+       if (!have_seeprom) {
+               u_int     nvram_scb;
+
+               /*
+                * Pull scratch ram settings and treat them as
+                * if they are the contents of an seeprom if
+                * the 'ADPT', 'BIOS', or 'ASPI' signature is found
+                * in SCB 0xFF.  We manually compose the data as 16bit
+                * values to avoid endian issues.
+                */
+               ahd_set_scbptr(ahd, 0xFF);
+               nvram_scb = ahd_inb_scbram(ahd, SCB_BASE + NVRAM_SCB_OFFSET);
+               printf("nvram_scb == 0x%x\n", nvram_scb);
+               printf("SCBPTR == 0x%x\n", ahd_get_scbptr(ahd));
+               printf("Signature = %c%c%c%c\n",
+                      ahd_inb_scbram(ahd, SCB_BASE + 0),
+                      ahd_inb_scbram(ahd, SCB_BASE + 1),
+                      ahd_inb_scbram(ahd, SCB_BASE + 2),
+                      ahd_inb_scbram(ahd, SCB_BASE + 3));
+               if (nvram_scb != 0xFF
+                && ((ahd_inb_scbram(ahd, SCB_BASE + 0) == 'A'
+                  && ahd_inb_scbram(ahd, SCB_BASE + 1) == 'D'
+                  && ahd_inb_scbram(ahd, SCB_BASE + 2) == 'P'
+                  && ahd_inb_scbram(ahd, SCB_BASE + 3) == 'T')
+                 || (ahd_inb_scbram(ahd, SCB_BASE + 0) == 'B'
+                  && ahd_inb_scbram(ahd, SCB_BASE + 1) == 'I'
+                  && ahd_inb_scbram(ahd, SCB_BASE + 2) == 'O'
+                  && ahd_inb_scbram(ahd, SCB_BASE + 3) == 'S')
+                 || (ahd_inb_scbram(ahd, SCB_BASE + 0) == 'A'
+                  && ahd_inb_scbram(ahd, SCB_BASE + 1) == 'S'
+                  && ahd_inb_scbram(ahd, SCB_BASE + 2) == 'P'
+                  && ahd_inb_scbram(ahd, SCB_BASE + 3) == 'I'))) {
+                       uint16_t *sc_data;
+                       int       i;
+
+                       ahd_set_scbptr(ahd, nvram_scb);
+                       sc_data = (uint16_t *)sc;
+                       for (i = 0; i < 64; i += 2)
+                               *sc_data++ = ahd_inw_scbram(ahd, SCB_BASE+i);
+                       have_seeprom = ahd_verify_cksum(sc);
+                       if (have_seeprom)
+                               ahd->flags |= AHD_SCB_CONFIG_USED;
+               }
+       }
+
+#ifdef AHD_DEBUG
+       if (have_seeprom != 0
+        && (ahd_debug & AHD_DUMP_SEEPROM) != 0) {
+               uint8_t *sc_data;
+               int      i;
+
+               printf("%s: Seeprom Contents:", ahd_name(ahd));
+               sc_data = (uint8_t *)sc;
+               for (i = 0; i < (sizeof(*sc)); i += 2)
+                       printf("\n\t0x%.4x", 
+                              sc_data[i] | (sc_data[i+1] << 8));
+               printf("\n");
+       }
+#endif
+
+       if (!have_seeprom) {
+               if (bootverbose)
+                       printf("%s: No SEEPROM available.\n", ahd_name(ahd));
+               ahd->flags |= AHD_USEDEFAULTS;
+               error = ahd_default_config(ahd);
+               adapter_control = CFAUTOTERM|CFSEAUTOTERM;
+               free(ahd->seep_config, M_DEVBUF);
+               ahd->seep_config = NULL;
+       } else {
+               error = ahd_parse_cfgdata(ahd, sc);
+               adapter_control = sc->adapter_control;
+       }
+       if (error != 0)
+               return (error);
+
+       ahd_configure_termination(ahd, adapter_control);
+
+       return (0);
+}
+
+static void
+ahd_configure_termination(struct ahd_softc *ahd, u_int adapter_control)
+{
+       int      error;
+       u_int    sxfrctl1;
+       uint8_t  termctl;
+       uint32_t devconfig;
+
+       devconfig = ahd_pci_read_config(ahd->dev_softc, DEVCONFIG, /*bytes*/4);
+       devconfig &= ~STPWLEVEL;
+       if ((ahd->flags & AHD_STPWLEVEL_A) != 0) {
+               devconfig |= STPWLEVEL;
+       }
+       ahd_pci_write_config(ahd->dev_softc, DEVCONFIG, devconfig, /*bytes*/4);
+       /* Make sure current sensing is off. */
+       if ((ahd->flags & AHD_CURRENT_SENSING) != 0) {
+               (void)ahd_write_flexport(ahd, FLXADDR_ROMSTAT_CURSENSECTL, 0);
+       }
+
+       /*
+        * Read to sense.  Write to set.
+        */
+       error = ahd_read_flexport(ahd, FLXADDR_TERMCTL, &termctl);
+       if ((adapter_control & CFAUTOTERM) == 0) {
+               if (bootverbose)
+                       printf("%s: Manual Primary Termination\n",
+                              ahd_name(ahd));
+               termctl &= ~(FLX_TERMCTL_ENPRILOW|FLX_TERMCTL_ENPRIHIGH);
+               if ((adapter_control & CFSTERM) != 0)
+                       termctl |= FLX_TERMCTL_ENPRILOW;
+               if ((adapter_control & CFWSTERM) != 0)
+                       termctl |= FLX_TERMCTL_ENPRIHIGH;
+       } else if (error != 0) {
+               printf("%s: Primary Auto-Term Sensing failed! "
+                      "Using Defaults.\n", ahd_name(ahd));
+               termctl = FLX_TERMCTL_ENPRILOW|FLX_TERMCTL_ENPRIHIGH;
+       }
+
+       if ((adapter_control & CFSEAUTOTERM) == 0) {
+               if (bootverbose)
+                       printf("%s: Manual Secondary Termination\n",
+                              ahd_name(ahd));
+               termctl &= ~(FLX_TERMCTL_ENSECLOW|FLX_TERMCTL_ENSECHIGH);
+               if ((adapter_control & CFSELOWTERM) != 0)
+                       termctl |= FLX_TERMCTL_ENSECLOW;
+               if ((adapter_control & CFSEHIGHTERM) != 0)
+                       termctl |= FLX_TERMCTL_ENSECHIGH;
+       } else if (error != 0) {
+               printf("%s: Secondary Auto-Term Sensing failed! "
+                      "Using Defaults.\n", ahd_name(ahd));
+               termctl |= FLX_TERMCTL_ENSECLOW|FLX_TERMCTL_ENSECHIGH;
+       }
+
+       /*
+        * Now set the termination based on what we found.
+        */
+       sxfrctl1 = ahd_inb(ahd, SXFRCTL1) & ~STPWEN;
+       if ((termctl & FLX_TERMCTL_ENPRILOW) != 0) {
+               ahd->flags |= AHD_TERM_ENB_A;
+               sxfrctl1 |= STPWEN;
+       }
+       /* Must set the latch once in order to be effective. */
+       ahd_outb(ahd, SXFRCTL1, sxfrctl1|STPWEN);
+       ahd_outb(ahd, SXFRCTL1, sxfrctl1);
+
+       error = ahd_write_flexport(ahd, FLXADDR_TERMCTL, termctl);
+       if (error != 0) {
+               printf("%s: Unable to set termination settings!\n",
+                      ahd_name(ahd));
+       } else if (bootverbose) {
+               printf("%s: Primary High byte termination %sabled\n",
+                      ahd_name(ahd),
+                      (termctl & FLX_TERMCTL_ENPRIHIGH) ? "En" : "Dis");
+
+               printf("%s: Primary Low byte termination %sabled\n",
+                      ahd_name(ahd),
+                      (termctl & FLX_TERMCTL_ENPRILOW) ? "En" : "Dis");
+
+               printf("%s: Secondary High byte termination %sabled\n",
+                      ahd_name(ahd),
+                      (termctl & FLX_TERMCTL_ENSECHIGH) ? "En" : "Dis");
+
+               printf("%s: Secondary Low byte termination %sabled\n",
+                      ahd_name(ahd),
+                      (termctl & FLX_TERMCTL_ENSECLOW) ? "En" : "Dis");
+       }
+       return;
+}
+
+#define        DPE     0x80
+#define SSE    0x40
+#define        RMA     0x20
+#define        RTA     0x10
+#define STA    0x08
+#define DPR    0x01
+
+static const char *split_status_source[] =
+{
+       "DFF0",
+       "DFF1",
+       "OVLY",
+       "CMC",
+};
+
+static const char *pci_status_source[] =
+{
+       "DFF0",
+       "DFF1",
+       "SG",
+       "CMC",
+       "OVLY",
+       "NONE",
+       "MSI",
+       "TARG"
+};
+
+static const char *split_status_strings[] =
+{
+       "%s: Received split response in %s.\n"
+       "%s: Received split completion error message in %s\n",
+       "%s: Receive overrun in %s\n",
+       "%s: Count not complete in %s\n",
+       "%s: Split completion data bucket in %s\n",
+       "%s: Split completion address error in %s\n",
+       "%s: Split completion byte count error in %s\n",
+       "%s: Signaled Target-abort to early terminate a split in %s\n",
+};
+
+static const char *pci_status_strings[] =
+{
+       "%s: Data Parity Error has been reported via PERR# in %s\n",
+       "%s: Target initial wait state error in %s\n",
+       "%s: Split completion read data parity error in %s\n",
+       "%s: Split completion address attribute parity error in %s\n",
+       "%s: Received a Target Abort in %s\n",
+       "%s: Received a Master Abort in %s\n",
+       "%s: Signal System Error Detected in %s\n",
+       "%s: Address or Write Phase Parity Error Detected in %s.\n"
+};
+
+void
+ahd_pci_intr(struct ahd_softc *ahd)
+{
+       uint8_t         pci_status[8];
+       ahd_mode_state  saved_modes;
+       u_int           pci_status1;
+       u_int           intstat;
+       u_int           i;
+       u_int           reg;
+       
+       intstat = ahd_inb(ahd, INTSTAT);
+
+       if ((intstat & SPLTINT) != 0)
+               ahd_pci_split_intr(ahd, intstat);
+
+       if ((intstat & PCIINT) == 0)
+               return;
+
+       printf("%s: PCI error Interrupt\n", ahd_name(ahd));
+       saved_modes = ahd_save_modes(ahd);
+       ahd_dump_card_state(ahd);
+       ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
+       for (i = 0, reg = DF0PCISTAT; i < 8; i++, reg++) {
+
+               if (i == 5)
+                       continue;
+               pci_status[i] = ahd_inb(ahd, reg);
+               /* Clear latched errors.  So our interupt deasserts. */
+               ahd_outb(ahd, reg, pci_status[i]);
+       }
+
+       for (i = 0; i < 8; i++) {
+               u_int bit;
+       
+               if (i == 5)
+                       continue;
+
+               for (bit = 0; bit < 8; bit++) {
+
+                       if ((pci_status[i] & (0x1 << bit)) != 0) {
+                               static const char *s;
+
+                               s = pci_status_strings[bit];
+                               if (i == 7/*TARG*/ && bit == 3)
+                                       s = "%s: Signal Target Abort\n";
+                               printf(s, ahd_name(ahd), pci_status_source[i]);
+                       }
+               }       
+       }
+       pci_status1 = ahd_pci_read_config(ahd->dev_softc,
+                                         PCIR_STATUS + 1, /*bytes*/1);
+       ahd_pci_write_config(ahd->dev_softc, PCIR_STATUS + 1,
+                            pci_status1, /*bytes*/1);
+       ahd_restore_modes(ahd, saved_modes);
+       ahd_unpause(ahd);
+}
+
+static void
+ahd_pci_split_intr(struct ahd_softc *ahd, u_int intstat)
+{
+       uint8_t         split_status[4];
+       uint8_t         split_status1[4];
+       uint8_t         sg_split_status[2];
+       uint8_t         sg_split_status1[2];
+       ahd_mode_state  saved_modes;
+       u_int           i;
+       uint16_t        pcix_status;
+
+       /*
+        * Check for splits in all modes.  Modes 0 and 1
+        * additionally have SG engine splits to look at.
+        */
+       pcix_status = ahd_pci_read_config(ahd->dev_softc, PCIXR_STATUS,
+                                         /*bytes*/2);
+       printf("%s: PCI Split Interrupt - PCI-X status = 0x%x\n",
+              ahd_name(ahd), pcix_status);
+       saved_modes = ahd_save_modes(ahd);
+       for (i = 0; i < 4; i++) {
+               ahd_set_modes(ahd, i, i);
+
+               split_status[i] = ahd_inb(ahd, DCHSPLTSTAT0);
+               split_status1[i] = ahd_inb(ahd, DCHSPLTSTAT1);
+               /* Clear latched errors.  So our interupt deasserts. */
+               ahd_outb(ahd, DCHSPLTSTAT0, split_status[i]);
+               ahd_outb(ahd, DCHSPLTSTAT1, split_status1[i]);
+               if (i != 0)
+                       continue;
+               sg_split_status[i] = ahd_inb(ahd, SGSPLTSTAT0);
+               sg_split_status1[i] = ahd_inb(ahd, SGSPLTSTAT1);
+               /* Clear latched errors.  So our interupt deasserts. */
+               ahd_outb(ahd, SGSPLTSTAT0, sg_split_status[i]);
+               ahd_outb(ahd, SGSPLTSTAT1, sg_split_status1[i]);
+       }
+
+       for (i = 0; i < 4; i++) {
+               u_int bit;
+
+               for (bit = 0; bit < 8; bit++) {
+
+                       if ((split_status[i] & (0x1 << bit)) != 0) {
+                               static const char *s;
+
+                               s = split_status_strings[bit];
+                               printf(s, ahd_name(ahd),
+                                      split_status_source[i]);
+                       }
+
+                       if (i != 0)
+                               continue;
+
+                       if ((sg_split_status[i] & (0x1 << bit)) != 0) {
+                               static const char *s;
+
+                               s = split_status_strings[bit];
+                               printf(s, ahd_name(ahd), "SG");
+                       }
+               }
+       }
+       /*
+        * Clear PCI-X status bits.
+        */
+       ahd_pci_write_config(ahd->dev_softc, PCIXR_STATUS,
+                            pcix_status, /*bytes*/2);
+       ahd_restore_modes(ahd, saved_modes);
+}
+
+static int
+ahd_aic7901_setup(struct ahd_softc *ahd)
+{
+       ahd_dev_softc_t pci;
+       
+       pci = ahd->dev_softc;
+       ahd->channel = 'A';
+       ahd->chip = AHD_AIC7901;
+       ahd->features = AHD_AIC7901_FE;
+       return (0);
+}
+
+static int
+ahd_aic7902_setup(struct ahd_softc *ahd)
+{
+       ahd_dev_softc_t pci;
+       u_int rev;
+       u_int devconfig1;
+
+       pci = ahd->dev_softc;
+       rev = ahd_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+       if (rev < ID_AIC7902_PCI_REV_A3) {
+               printf("%s: Unable to attach to unsupported chip revision %d\n",
+                      ahd_name(ahd), rev);
+               ahd_pci_write_config(pci, PCIR_COMMAND, 0, /*bytes*/1);
+               return (ENXIO);
+       }
+       if (rev < ID_AIC7902_PCI_REV_B0) {
+               /*
+                * Pending request assertion does not work on the A if we have
+                * DMA requests outstanding on both channels.  See H2A3 Razors
+                * #327 and #365.
+                */
+               devconfig1 = ahd_pci_read_config(pci, DEVCONFIG1, /*bytes*/1);
+               ahd_pci_write_config(pci, DEVCONFIG1,
+                                    devconfig1|PREQDIS, /*bytes*/1);
+               devconfig1 = ahd_pci_read_config(pci, DEVCONFIG1, /*bytes*/1);
+               /*
+                * Enable A series workarounds.
+                */
+               ahd->bugs |= AHD_SENT_SCB_UPDATE_BUG|AHD_ABORT_LQI_BUG
+                         |  AHD_PKT_BITBUCKET_BUG|AHD_LONG_SETIMO_BUG
+                         |  AHD_NLQICRC_DELAYED_BUG|AHD_SCSIRST_BUG
+                         |  AHD_LQO_ATNO_BUG|AHD_AUTOFLUSH_BUG
+                         |  AHD_CLRLQO_AUTOCLR_BUG|AHD_PCIX_MMAPIO_BUG
+                         |  AHD_PCIX_CHIPRST_BUG|AHD_PKTIZED_STATUS_BUG;
+               if (rev < ID_AIC7902_PCI_REV_A4)
+                       ahd->bugs |= AHD_PCIX_ARBITER_BUG|AHD_PCIX_SPLIT_BUG;
+       }
+
+       ahd->channel = ahd_get_pci_function(pci) + 'A';
+       ahd->chip = AHD_AIC7902;
+       ahd->features = AHD_AIC7902_FE;
+       return (0);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_proc.c b/xen/drivers/scsi/aic7xxx/aic79xx_proc.c
new file mode 100644 (file)
index 0000000..a9b05d4
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * String handling code courtesy of Gerard Roudier's <groudier@club-internet.fr>
+ * sym driver.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_proc.c#7 $
+ */
+#include "aic79xx_osm.h"
+#include "aic79xx_inline.h"
+
+static void    copy_mem_info(struct info_str *info, char *data, int len);
+static int     copy_info(struct info_str *info, char *fmt, ...);
+static u_int   scsi_calc_syncsrate(u_int period_factor);
+static void    ahd_dump_target_state(struct ahd_softc *ahd,
+                                     struct info_str *info,
+                                     u_int our_id, char channel,
+                                     u_int target_id, u_int target_offset);
+static void    ahd_dump_device_state(struct info_str *info,
+                                     struct ahd_linux_device *dev);
+static int     ahd_proc_write_seeprom(struct ahd_softc *ahd,
+                                      char *buffer, int length);
+
+static void
+copy_mem_info(struct info_str *info, char *data, int len)
+{
+       if (info->pos + len > info->offset + info->length)
+               len = info->offset + info->length - info->pos;
+
+       if (info->pos + len < info->offset) {
+               info->pos += len;
+               return;
+       }
+
+       if (info->pos < info->offset) {
+               off_t partial;
+
+               partial = info->offset - info->pos;
+               data += partial;
+               info->pos += partial;
+               len  -= partial;
+       }
+
+       if (len > 0) {
+               memcpy(info->buffer, data, len);
+               info->pos += len;
+               info->buffer += len;
+       }
+}
+
+static int
+copy_info(struct info_str *info, char *fmt, ...)
+{
+       va_list args;
+       char buf[256];
+       int len;
+
+       va_start(args, fmt);
+       len = vsprintf(buf, fmt, args);
+       va_end(args);
+
+       copy_mem_info(info, buf, len);
+       return (len);
+}
+
+/*
+ * Table of syncrates that don't follow the "divisible by 4"
+ * rule. This table will be expanded in future SCSI specs.
+ */
+static struct {
+       u_int period_factor;
+       u_int period;   /* in 100ths of ns */
+} scsi_syncrates[] = {
+       { 0x08,  625 }, /* FAST-160 */
+       { 0x09, 1250 }, /* FAST-80 */
+       { 0x0a, 2500 }, /* FAST-40 40MHz */
+       { 0x0b, 3030 }, /* FAST-40 33MHz */
+       { 0x0c, 5000 }  /* FAST-20 */
+};
+/*
+ * Return the frequency in kHz corresponding to the given
+ * sync period factor.
+ */
+static u_int
+scsi_calc_syncsrate(u_int period_factor)
+{
+       int i; 
+       int num_syncrates;
+       num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]);
+       /* See if the period is in the "exception" table */
+       for (i = 0; i < num_syncrates; i++) {
+
+               if (period_factor == scsi_syncrates[i].period_factor) {
+                               /* Period in kHz */
+                       return (100000000 / scsi_syncrates[i].period);
+               }
+       }
+
+       /*
+        * Wasn't in the table, so use the standard
+        * 4 times conversion.
+        */
+       return (10000000 / (period_factor * 4 * 10));
+}
+
+void
+ahd_format_transinfo(struct info_str *info, struct ahd_transinfo *tinfo)
+{
+       u_int speed;
+       u_int freq;
+       u_int mb;
+
+        speed = 3300;
+        freq = 0;
+       if (tinfo->offset != 0) {
+               freq = scsi_calc_syncsrate(tinfo->period);
+               speed = freq;
+       }
+       speed *= (0x01 << tinfo->width);
+        mb = speed / 1000;
+        if (mb > 0)
+               copy_info(info, "%d.%03dMB/s transfers", mb, speed % 1000);
+        else
+               copy_info(info, "%dKB/s transfers", speed);
+
+       if (freq != 0) {
+               copy_info(info, " (%d.%03dMHz%s, offset %d",
+                        freq / 1000, freq % 1000,
+                        (tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0
+                        ? " DT" : "", tinfo->offset);
+       }
+
+       if (tinfo->width > 0) {
+               if (freq != 0) {
+                       copy_info(info, ", ");
+               } else {
+                       copy_info(info, " (");
+               }
+               copy_info(info, "%dbit)", 8 * (0x01 << tinfo->width));
+       } else if (freq != 0) {
+               copy_info(info, ")");
+       }
+       copy_info(info, "\n");
+}
+
+static void
+ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info,
+                     u_int our_id, char channel, u_int target_id,
+                     u_int target_offset)
+{
+       struct  ahd_linux_target *targ;
+       struct  ahd_initiator_tinfo *tinfo;
+       struct  ahd_tmode_tstate *tstate;
+       int     lun;
+
+       tinfo = ahd_fetch_transinfo(ahd, channel, our_id,
+                                   target_id, &tstate);
+       copy_info(info, "Channel %c Target %d Negotiation Settings\n",
+                 channel, target_id);
+       copy_info(info, "\tUser: ");
+       ahd_format_transinfo(info, &tinfo->user);
+       targ = ahd->platform_data->targets[target_offset];
+       if (targ == NULL)
+               return;
+
+       copy_info(info, "\tGoal: ");
+       ahd_format_transinfo(info, &tinfo->goal);
+       copy_info(info, "\tCurr: ");
+       ahd_format_transinfo(info, &tinfo->curr);
+
+       for (lun = 0; lun < AHD_NUM_LUNS; lun++) {
+               struct ahd_linux_device *dev;
+
+               dev = targ->devices[lun];
+
+               if (dev == NULL)
+                       continue;
+
+               ahd_dump_device_state(info, dev);
+       }
+}
+
+static void
+ahd_dump_device_state(struct info_str *info, struct ahd_linux_device *dev)
+{
+       copy_info(info, "\tChannel %c Target %d Lun %d Settings\n",
+                 dev->target->channel + 'A', dev->target->target, dev->lun);
+
+       copy_info(info, "\t\tCommands Queued %ld\n", dev->commands_issued);
+       copy_info(info, "\t\tCommands Active %d\n", dev->active);
+       copy_info(info, "\t\tCommand Openings %d\n", dev->openings);
+       copy_info(info, "\t\tMax Tagged Openings %d\n", dev->maxtags);
+       copy_info(info, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
+}
+
+static int
+ahd_proc_write_seeprom(struct ahd_softc *ahd, char *buffer, int length)
+{
+       ahd_mode_state saved_modes;
+       int have_seeprom;
+       u_long s;
+       int paused;
+       int written;
+
+       /* Default to failure. */
+       written = -EINVAL;
+       ahd_lock(ahd, &s);
+       paused = ahd_is_paused(ahd);
+       if (!paused)
+               ahd_pause(ahd);
+
+       saved_modes = ahd_save_modes(ahd);
+       ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
+       if (length != sizeof(struct seeprom_config)) {
+               printf("ahd_proc_write_seeprom: incorrect buffer size\n");
+               goto done;
+       }
+
+       have_seeprom = ahd_verify_cksum((struct seeprom_config*)buffer);
+       if (have_seeprom == 0) {
+               printf("ahd_proc_write_seeprom: cksum verification failed\n");
+               goto done;
+       }
+
+       have_seeprom = ahd_acquire_seeprom(ahd);
+       if (!have_seeprom) {
+               printf("ahd_proc_write_seeprom: No Serial EEPROM\n");
+               goto done;
+       } else {
+               u_int start_addr;
+
+               if (ahd->seep_config == NULL) {
+                       ahd->seep_config = malloc(sizeof(*ahd->seep_config),
+                                                 M_DEVBUF, M_NOWAIT);
+                       if (ahd->seep_config == NULL) {
+                               printf("aic79xx: Unable to allocate serial "
+                                      "eeprom buffer.  Write failing\n");
+                               goto done;
+                       }
+               }
+               printf("aic79xx: Writing Serial EEPROM\n");
+               start_addr = 32 * (ahd->channel - 'A');
+               ahd_write_seeprom(ahd, (u_int16_t *)buffer, start_addr,
+                                 sizeof(struct seeprom_config)/2);
+               ahd_read_seeprom(ahd, (uint16_t *)ahd->seep_config,
+                                start_addr, sizeof(struct seeprom_config)/2);
+               ahd_release_seeprom(ahd);
+               written = length;
+       }
+
+done:
+       ahd_restore_modes(ahd, saved_modes);
+       if (!paused)
+               ahd_unpause(ahd);
+       ahd_unlock(ahd, &s);
+       return (written);
+}
+/*
+ * Return information to handle /proc support for the driver.
+ */
+int
+ahd_linux_proc_info(char *buffer, char **start, off_t offset,
+                 int length, int hostno, int inout)
+{
+       struct  ahd_softc *ahd;
+       struct  info_str info;
+       char    ahd_info[256];
+       u_long  l;
+       u_int   max_targ;
+       u_int   i;
+       int     retval;
+
+       retval = -EINVAL;
+       ahd_list_lock(&l);
+       TAILQ_FOREACH(ahd, &ahd_tailq, links) {
+               if (ahd->platform_data->host->host_no == hostno)
+                       break;
+       }
+
+       if (ahd == NULL)
+               goto done;
+
+        /* Has data been written to the file? */ 
+       if (inout == TRUE) {
+               retval = ahd_proc_write_seeprom(ahd, buffer, length);
+               goto done;
+       }
+
+       if (start)
+               *start = buffer;
+
+       info.buffer     = buffer;
+       info.length     = length;
+       info.offset     = offset;
+       info.pos        = 0;
+
+       copy_info(&info, "Adaptec AIC79xx driver version: %s\n",
+                 AIC79XX_DRIVER_VERSION);
+       ahd_controller_info(ahd, ahd_info);
+       copy_info(&info, "%s\n\n", ahd_info);
+
+       if (ahd->seep_config == NULL)
+               copy_info(&info, "No Serial EEPROM\n");
+       else {
+               copy_info(&info, "Serial EEPROM:\n");
+               for (i = 0; i < sizeof(*ahd->seep_config)/2; i++) {
+                       if (((i % 8) == 0) && (i != 0)) {
+                               copy_info(&info, "\n");
+                       }
+                       copy_info(&info, "0x%.4x ",
+                                 ((uint16_t*)ahd->seep_config)[i]);
+               }
+               copy_info(&info, "\n");
+       }
+       copy_info(&info, "\n");
+
+       max_targ = 15;
+       if ((ahd->features & AHD_WIDE) == 0)
+               max_targ = 7;
+
+       for (i = 0; i <= max_targ; i++) {
+
+               ahd_dump_target_state(ahd, &info, ahd->our_id, 'A',
+                                     /*target_id*/i, /*target_offset*/i);
+       }
+       retval = info.pos > info.offset ? info.pos - info.offset : 0;
+done:
+       ahd_list_unlock(&l);
+       return (retval);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_reg.h b/xen/drivers/scsi/aic7xxx/aic79xx_reg.h
new file mode 100644 (file)
index 0000000..45852b5
--- /dev/null
@@ -0,0 +1,1357 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ *              from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#42 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#34 $
+ */
+
+#define        MODE_PTR                        0x00
+#define                DST_MODE                0x70
+#define                SRC_MODE                0x07
+
+#define        INTSTAT                         0x01
+#define                INT_PEND                0xff
+#define                HWERRINT                0x80
+#define                BRKADRINT               0x40
+#define                SWTMINT                 0x20
+#define                PCIINT                  0x10
+#define                SCSIINT                 0x08
+#define                SEQINT                  0x04
+#define                CMDCMPLT                0x02
+#define                SPLTINT                 0x01
+
+#define        SEQINTCODE                      0x02
+#define                PCIX_ARBITOR_WW         0x19
+#define                ENTERING_NONPACK        0x18
+#define                MONITORDRAIN            0x17
+#define                SNAPSHOTCLRCHN          0x16
+#define                CFG4OVERRUN             0x15
+#define                STATUS_OVERRUN          0x14
+#define                CFG4ISTAT_INTR          0x13
+#define                INVALID_SEQINT          0x12
+#define                ILLEGAL_PHASE           0x11
+#define                DUMP_CARD_STATE         0x10
+#define                NO_FREE_FIFO            0x0f
+#define                OUT_OF_RANGE            0x0e
+#define                NO_FREE_SCB             0x0d
+#define                SCB_MISMATCH            0x0c
+#define                MISSED_BUSFREE          0x0b
+#define                MKMSG_FAILED            0x0a
+#define                DATA_OVERRUN            0x09
+#define                BAD_STATUS              0x08
+#define                HOST_MSG_LOOP           0x07
+#define                PDATA_REINIT            0x06
+#define                IGN_WIDE_RES            0x05
+#define                NO_MATCH                0x04
+#define                PROTO_VIOLATION         0x03
+#define                SEND_REJECT             0x02
+#define                BAD_PHASE               0x01
+
+#define        CLRINT                          0x03
+#define                CLRBRKADRINT            0x40
+#define                CLRSWTMINT              0x20
+#define                CLRSCSIINT              0x08
+#define                CLRSEQINT               0x04
+#define                CLRCMDINT               0x02
+#define                CLRSPLTINT              0x01
+
+#define        ERROR                           0x04
+#define                CIOPARERR               0x80
+#define                MPARERR                 0x20
+#define                DPARERR                 0x10
+#define                SQPARERR                0x08
+#define                ILLOPCODE               0x04
+#define                DSCTMOUT                0x02
+
+#define        CLRERR                          0x04
+#define                CLRCIOPARERR            0x80
+#define                CLRMPARERR              0x20
+#define                CLRDPARERR              0x10
+#define                CLRSQPARERR             0x08
+#define                CLRILLOPCODE            0x04
+#define                CLRDSCTMOUT             0x02
+
+#define        HCNTRL                          0x05
+#define                POWRDN                  0x40
+#define                SWINT                   0x10
+#define                HCNTRL3                 0x08
+#define                PAUSE                   0x04
+#define                INTEN                   0x02
+#define                CHIPRST                 0x01
+#define                CHIPRSTACK              0x01
+
+#define        HNSCB_QOFF                      0x06
+
+#define        HESCB_QOFF                      0x08
+
+#define        HS_MAILBOX                      0x0b
+#define                HOST_TQINPOS            0x80
+
+#define        SEQINTSTAT                      0x0c
+#define                SEQ_SWTMRTO             0x10
+#define                SEQ_SEQINT              0x08
+#define                SEQ_SCSIINT             0x04
+#define                SEQ_PCIINT              0x02
+#define                SEQ_SPLTINT             0x01
+
+#define        SNSCB_QOFF                      0x10
+
+#define        SESCB_QOFF                      0x12
+
+#define        SDSCB_QOFF                      0x14
+
+#define        QOFF_CTLSTA                     0x16
+#define                EMPTY_SCB_AVAIL         0x80
+#define                NEW_SCB_AVAIL           0x40
+#define                SDSCB_ROLLOVR           0x20
+#define                HS_MAILBOX_ACT          0x10
+#define                SCB_QSIZE               0x0f
+#define                SCB_QSIZE_16384         0x0c
+#define                SCB_QSIZE_8192          0x0b
+#define                SCB_QSIZE_4096          0x0a
+#define                SCB_QSIZE_2048          0x09
+#define                SCB_QSIZE_1024          0x08
+#define                SCB_QSIZE_512           0x07
+#define                SCB_QSIZE_256           0x06
+#define                SCB_QSIZE_128           0x05
+#define                SCB_QSIZE_64            0x04
+#define                SCB_QSIZE_32            0x03
+#define                SCB_QSIZE_16            0x02
+#define                SCB_QSIZE_8             0x01
+#define                SCB_QSIZE_4             0x00
+
+#define        INTCTL                          0x18
+#define                SWTMINTMASK             0x80
+#define                SWTMINTEN               0x40
+#define                SWTIMER_START           0x20
+#define                AUTOCLRCMDINT           0x10
+#define                PCIINTEN                0x08
+#define                SCSIINTEN               0x04
+#define                SEQINTEN                0x02
+#define                SPLTINTEN               0x01
+
+#define        DFCNTRL                         0x19
+#define                SCSIENACK               0x20
+#define                DIRECTIONACK            0x04
+#define                FIFOFLUSHACK            0x02
+#define                DIRECTIONEN             0x01
+
+#define        DSCOMMAND0                      0x19
+#define                CACHETHEN               0x80
+#define                DPARCKEN                0x40
+#define                MPARCKEN                0x20
+#define                EXTREQLCK               0x10
+#define                CIOPARCKEN              0x01
+
+#define        DFSTATUS                        0x1a
+#define                PRELOAD_AVAIL           0x80
+#define                PKT_PRELOAD_AVAIL       0x40
+#define                MREQPEND                0x10
+#define                HDONE                   0x08
+#define                DFTHRESH                0x04
+#define                FIFOFULL                0x02
+#define                FIFOEMP                 0x01
+
+#define        SG_CACHE_SHADOW                 0x1b
+#define                ODD_SEG                 0x04
+#define                LAST_SEG                0x02
+#define                LAST_SEG_DONE           0x01
+
+#define        ARBCTL                          0x1b
+#define                RESET_HARB              0x80
+#define                RETRY_SWEN              0x08
+#define                USE_TIME                0x07
+
+#define        SG_CACHE_PRE                    0x1b
+
+#define        LQIN                            0x20
+
+#define        TYPEPTR                         0x20
+
+#define        TAGPTR                          0x21
+
+#define        LUNPTR                          0x22
+
+#define        DATALENPTR                      0x23
+
+#define        STATLENPTR                      0x24
+
+#define        CMDLENPTR                       0x25
+
+#define        ATTRPTR                         0x26
+
+#define        FLAGPTR                         0x27
+
+#define        CMDPTR                          0x28
+
+#define        QNEXTPTR                        0x29
+
+#define        IDPTR                           0x2a
+
+#define        ABRTBYTEPTR                     0x2b
+
+#define        ABRTBITPTR                      0x2c
+
+#define        LUNLEN                          0x30
+
+#define        CDBLIMIT                        0x31
+
+#define        MAXCMD                          0x32
+
+#define        MAXCMDCNT                       0x33
+
+#define        LQRSVD01                        0x34
+
+#define        LQRSVD16                        0x35
+
+#define        LQRSVD17                        0x36
+
+#define        CMDRSVD0                        0x37
+
+#define        LQCTL0                          0x38
+#define                LQITARGCLT              0xc0
+#define                LQIINITGCLT             0x30
+#define                LQ0TARGCLT              0x0c
+#define                LQ0INITGCLT             0x03
+
+#define        LQCTL1                          0x38
+#define                PCI2PCI                 0x04
+#define                SINGLECMD               0x02
+#define                ABORTPENDING            0x01
+
+#define        SCSBIST0                        0x39
+#define                GSBISTERR               0x40
+#define                GSBISTDONE              0x20
+#define                GSBISTRUN               0x10
+#define                OSBISTERR               0x04
+#define                OSBISTDONE              0x02
+#define                OSBISTRUN               0x01
+
+#define        LQCTL2                          0x39
+#define                LQIRETRY                0x80
+#define                LQICONTINUE             0x40
+#define                LQITOIDLE               0x20
+#define                LQIPAUSE                0x10
+#define                LQORETRY                0x08
+#define                LQOCONTINUE             0x04
+#define                LQOTOIDLE               0x02
+#define                LQOPAUSE                0x01
+
+#define        SCSBIST1                        0x3a
+#define                NTBISTERR               0x04
+#define                NTBISTDONE              0x02
+#define                NTBISTRUN               0x01
+
+#define        SCSISEQ0                        0x3a
+#define                TEMODEO                 0x80
+#define                ENSELO                  0x40
+#define                ENARBO                  0x20
+#define                FORCEBUSFREE            0x10
+#define                SCSIRSTO                0x01
+
+#define        SCSISEQ1                        0x3b
+
+#define        SXFRCTL0                        0x3c
+#define                DFON                    0x80
+#define                DFPEXP                  0x40
+#define                BIOSCANCELEN            0x10
+#define                SPIOEN                  0x08
+
+#define        BUSINITID                       0x3c
+
+#define        DLCOUNT                         0x3c
+
+#define        SXFRCTL1                        0x3d
+#define                BITBUCKET               0x80
+#define                ENSACHK                 0x40
+#define                ENSPCHK                 0x20
+#define                STIMESEL                0x18
+#define                ENSTIMER                0x04
+#define                ACTNEGEN                0x02
+#define                STPWEN                  0x01
+
+#define        BUSTARGID                       0x3e
+
+#define        SXFRCTL2                        0x3e
+#define                AUTORSTDIS              0x10
+#define                CMDDMAEN                0x08
+#define                ASU                     0x07
+
+#define        DFFSTAT                         0x3f
+#define                FIFO1FREE               0x20
+#define                FIFO0FREE               0x10
+#define                CURRFIFO                0x01
+
+#define        SCSISIGO                        0x40
+#define                CDO                     0x80
+#define                IOO                     0x40
+#define                MSGO                    0x20
+#define                ATNO                    0x10
+#define                SELO                    0x08
+#define                BSYO                    0x04
+#define                REQO                    0x02
+#define                ACKO                    0x01
+
+#define        MULTARGID                       0x40
+
+#define        SCSISIGI                        0x41
+#define                ATNI                    0x10
+#define                SELI                    0x08
+#define                BSYI                    0x04
+#define                REQI                    0x02
+#define                ACKI                    0x01
+
+#define        SCSIPHASE                       0x42
+#define                STATUS_PHASE            0x20
+#define                COMMAND_PHASE           0x10
+#define                MSG_IN_PHASE            0x08
+#define                MSG_OUT_PHASE           0x04
+#define                DATA_PHASE_MASK         0x03
+#define                DATA_IN_PHASE           0x02
+#define                DATA_OUT_PHASE          0x01
+
+#define        SCSIDAT0_IMG                    0x43
+
+#define        SCSIDAT                         0x44
+
+#define        SCSIBUS                         0x46
+
+#define        TARGIDIN                        0x48
+#define                CLKOUT                  0x80
+#define                TARGID                  0x0f
+
+#define        SELID                           0x49
+#define                SELID_MASK              0xf0
+#define                ONEBIT                  0x08
+
+#define        SBLKCTL                         0x4a
+#define                DIAGLEDEN               0x80
+#define                DIAGLEDON               0x40
+#define                ENAB40                  0x08
+#define                ENAB20                  0x04
+#define                SELWIDE                 0x02
+
+#define        OPTIONMODE                      0x4a
+#define                BIOSCANCTL              0x80
+#define                AUTOACKEN               0x40
+#define                BIASCANCTL              0x20
+#define                BUSFREEREV              0x10
+#define                ENDGFORMCHK             0x04
+#define                OPTIONMODE_DEFAULTS     0x02
+#define                AUTO_MSGOUT_DE          0x02
+
+#define        SSTAT0                          0x4b
+#define                TARGET                  0x80
+#define                SELDO                   0x40
+#define                SELDI                   0x20
+#define                SELINGO                 0x10
+#define                IOERR                   0x08
+#define                OVERRUN                 0x04
+#define                SPIORDY                 0x02
+#define                ARBDO                   0x01
+
+#define        CLRSINT0                        0x4b
+#define                CLRSELDO                0x40
+#define                CLRSELDI                0x20
+#define                CLRSELINGO              0x10
+#define                CLRIOERR                0x08
+#define                CLROVERRUN              0x04
+#define                CLRSPIORDY              0x02
+#define                CLRARBDO                0x01
+
+#define        SIMODE0                         0x4b
+#define                ENSELDO                 0x40
+#define                ENSELDI                 0x20
+#define                ENSELINGO               0x10
+#define                ENIOERR                 0x08
+#define                ENOVERRUN               0x04
+#define                ENSPIORDY               0x02
+#define                ENARBDO                 0x01
+
+#define        CLRSINT1                        0x4c
+#define                CLRSELTIMEO             0x80
+#define                CLRATNO                 0x40
+#define                CLRSCSIRSTI             0x20
+#define                CLRBUSFREE              0x08
+#define                CLRSCSIPERR             0x04
+#define                CLRSTRB2FAST            0x02
+#define                CLRREQINIT              0x01
+
+#define        SSTAT1                          0x4c
+#define                SELTO                   0x80
+#define                ATNTARG                 0x40
+#define                SCSIRSTI                0x20
+#define                PHASEMIS                0x10
+#define                BUSFREE                 0x08
+#define                SCSIPERR                0x04
+#define                STRB2FAST               0x02
+#define                REQINIT                 0x01
+
+#define        SSTAT2                          0x4d
+#define                BUSFREE_DFF1            0xc0
+#define                BUSFREETIME             0xc0
+#define                BUSFREE_DFF0            0x80
+#define                BUSFREE_LQO             0x40
+#define                NONPACKREQ              0x20
+#define                EXP_ACTIVE              0x10
+#define                BSYX                    0x08
+#define                WIDE_RES                0x04
+#define                SDONE                   0x02
+#define                DMADONE                 0x01
+
+#define        CLRSINT2                        0x4d
+#define                CLRNONPACKREQ           0x20
+#define                CLRWIDE_RES             0x04
+#define                CLRSDONE                0x02
+#define                CLRDMADONE              0x01
+
+#define        SIMODE2                         0x4d
+#define                ENWIDE_RES              0x04
+#define                ENSDONE                 0x02
+#define                ENDMADONE               0x01
+
+#define        PERRDIAG                        0x4e
+#define                HIZERO                  0x80
+#define                HIPERR                  0x40
+#define                PREVPHASE               0x20
+#define                PARITYERR               0x10
+#define                AIPERR                  0x08
+#define                CRCERR                  0x04
+#define                DGFORMERR               0x02
+#define                DTERR                   0x01
+
+#define        LQISTATE                        0x4e
+
+#define        SOFFCNT                         0x4f
+
+#define        LQOSTATE                        0x4f
+
+#define        LQISTAT0                        0x50
+#define                LQIATNQAS               0x20
+#define                LQICRCT1                0x10
+#define                LQICRCT2                0x08
+#define                LQIBADLQT               0x04
+#define                LQIATNLQ                0x02
+#define                LQIATNCMD               0x01
+
+#define        CLRLQIINTO                      0x50
+#define                CLRLQIATNQAS            0x20
+#define                CLRLQICRCT1             0x10
+#define                CLRLQICRCT2             0x08
+#define                CLRLQIBADLQT            0x04
+#define                CLRLQIATNLQ             0x02
+#define                CLRLQIATNCMD            0x01
+
+#define        LQIMODE0                        0x50
+#define                ENLQIATNQASK            0x20
+#define                ENLQICRCT1              0x10
+#define                ENLQICRCT2              0x08
+#define                ENLQIBADLQT             0x04
+#define                ENLQIATNLQ              0x02
+#define                ENLQIATNCMD             0x01
+
+#define        LQIMODE1                        0x51
+#define                ENLQIPHASE_LQ           0x80
+#define                ENLQIPHASE_NLQ          0x40
+#define                ENLIQABORT              0x20
+#define                ENLQICRCI_LQ            0x10
+#define                ENLQICRCI_NLQ           0x08
+#define                ENLQIBADLQI             0x04
+#define                ENLQIOVERI_LQ           0x02
+#define                ENLQIOVERI_NLQ          0x01
+
+#define        LQISTAT1                        0x51
+#define                LQIPHASE_LQ             0x80
+#define                LQIPHASE_NLQ            0x40
+#define                LQIABORT                0x20
+#define                LQICRCI_LQ              0x10
+#define                LQICRCI_NLQ             0x08
+#define                LQIBADLQI               0x04
+#define                LQIOVERI_LQ             0x02
+#define                LQIOVERI_NLQ            0x01
+
+#define        CLRLQIINT1                      0x51
+#define                CLRLQIPHASE_LQ          0x80
+#define                CLRLQIPHASE_NLQ         0x40
+#define                CLRLIQABORT             0x20
+#define                CLRLQICRCI_LQ           0x10
+#define                CLRLQICRCI_NLQ          0x08
+#define                CLRLQIBADLQI            0x04
+#define                CLRLQIOVERI_LQ          0x02
+#define                CLRLQIOVERI_NLQ         0x01
+
+#define        LQISTAT2                        0x52
+#define                PACKETIZED              0x80
+#define                LQIPHASE_OUTPKT         0x40
+#define                LQIWORKONLQ             0x20
+#define                LQIWAITFIFO             0x10
+#define                LQISTOPPKT              0x08
+#define                LQISTOPLQ               0x04
+#define                LQISTOPCMD              0x02
+#define                LQIGSAVAIL              0x01
+
+#define        SSTAT3                          0x53
+#define                NTRAMPERR               0x02
+#define                OSRAMPERR               0x01
+
+#define        SIMODE3                         0x53
+#define                ENNTRAMPERR             0x02
+#define                ENOSRAMPERR             0x01
+
+#define        CLRSINT3                        0x53
+#define                CLRNTRAMPERR            0x02
+#define                CLROSRAMPERR            0x01
+
+#define        LQOMODE0                        0x54
+#define                ENLQOTARGSCBPERR        0x10
+#define                ENLQOSTOPT2             0x08
+#define                ENLQOATNLQ              0x04
+#define                ENLQOATNPKT             0x02
+#define                ENLQOTCRC               0x01
+
+#define        LQOSTAT0                        0x54
+#define                LQOTARGSCBPERR          0x10
+#define                LQOSTOPT2               0x08
+#define                LQOATNLQ                0x04
+#define                LQOATNPKT               0x02
+#define                LQOTCRC                 0x01
+
+#define        CLRLQOINT0                      0x54
+#define                CLRLQOTARGSCBPERR       0x10
+#define                CLRLQOSTOPT2            0x08
+#define                CLRLQOATNLQ             0x04
+#define                CLRLQOATNPKT            0x02
+#define                CLRLQOTCRC              0x01
+
+#define        LQOSTAT1                        0x55
+#define                LQOINITSCBPERR          0x10
+#define                LQOSTOPI2               0x08
+#define                LQOBADQAS               0x04
+#define                LQOBUSFREE              0x02
+#define                LQOPHACHGINPKT          0x01
+
+#define        CLRLQOINT1                      0x55
+#define                CLRLQOINITSCBPERR       0x10
+#define                CLRLQOSTOPI2            0x08
+#define                CLRLQOBADQAS            0x04
+#define                CLRLQOBUSFREE           0x02
+#define                CLRLQOPHACHGINPKT       0x01
+
+#define        LQOMODE1                        0x55
+#define                ENLQOINITSCBPERR        0x10
+#define                ENLQOSTOPI2             0x08
+#define                ENLQOBADQAS             0x04
+#define                ENLQOBUSFREE            0x02
+#define                ENLQOPHACHGINPKT        0x01
+
+#define        LQOSTAT2                        0x56
+#define                LQOPKT                  0xe0
+#define                LQOWAITFIFO             0x10
+#define                LQOPHACHGOUTPKT         0x02
+#define                LQOSTOP0                0x01
+
+#define        OS_SPACE_CNT                    0x56
+
+#define        SIMODE1                         0x57
+#define                ENSELTIMO               0x80
+#define                ENATNTARG               0x40
+#define                ENSCSIRST               0x20
+#define                ENPHASEMIS              0x10
+#define                ENBUSFREE               0x08
+#define                ENSCSIPERR              0x04
+#define                ENSTRB2FAST             0x02
+#define                ENREQINIT               0x01
+
+#define        GSFIFO                          0x58
+
+#define        DFFSXFRCTL                      0x5a
+#define                CLRSHCNT                0x04
+#define                CLRCHN                  0x02
+#define                RSTCHN                  0x01
+
+#define        NEXTSCB                         0x5a
+
+#define        CLRSEQINTSRC                    0x5b
+#define                CLRCTXTDONE             0x40
+#define                CLRSAVEPTRS             0x20
+#define                CLRCFG4DATA             0x10
+#define                CLRCFG4ISTAT            0x08
+#define                CLRCFG4TSTAT            0x04
+#define                CLRCFG4ICMD             0x02
+#define                CLRCFG4TCMD             0x01
+
+#define        SEQINTSRC                       0x5b
+#define                CTXTDONE                0x40
+#define                SAVEPTRS                0x20
+#define                CFG4DATA                0x10
+#define                CFG4ISTAT               0x08
+#define                CFG4TSTAT               0x04
+#define                CFG4ICMD                0x02
+#define                CFG4TCMD                0x01
+
+#define        CURRSCB                         0x5c
+
+#define        SEQIMODE                        0x5c
+#define                ENCTXTDONE              0x40
+#define                ENSAVEPTRS              0x20
+#define                ENCFG4DATA              0x10
+#define                ENCFG4ISTAT             0x08
+#define                ENCFG4TSTAT             0x04
+#define                ENCFG4ICMD              0x02
+#define                ENCFG4TCMD              0x01
+
+#define        MDFFSTAT                        0x5d
+#define                LASTSDONE               0x10
+#define                SHVALID                 0x08
+#define                DLZERO                  0x04
+#define                DATAINFIFO              0x02
+#define                FIFOFREE                0x01
+
+#define        CRCCONTROL                      0x5d
+#define                CRCVALCHKEN             0x40
+
+#define        DFFTAG                          0x5e
+
+#define        LASTSCB                         0x5e
+
+#define        SCSITEST                        0x5e
+#define                CNTRTEST                0x08
+#define                SEL_TXPLL_DEBUG         0x04
+
+#define        IOPDNCTL                        0x5f
+#define                DISABLE_OE              0x80
+#define                PDN_IDIST               0x04
+#define                PDN_DIFFSENSE           0x01
+
+#define        SHADDR                          0x60
+
+#define        NEGOADDR                        0x60
+
+#define        DGRPCRCI                        0x60
+
+#define        NEGPERIOD                       0x61
+
+#define        PACKCRCI                        0x62
+
+#define        NEGOFFSET                       0x62
+
+#define        NEGPPROPTS                      0x63
+#define                PPROPT_PACE             0x08
+#define                PPROPT_QAS              0x04
+#define                PPROPT_DT               0x02
+#define                PPROPT_IUT              0x01
+
+#define        NEGCONOPTS                      0x64
+#define                ENAIP                   0x08
+#define                ENAUTOATNI              0x04
+#define                ENAUTOATNO              0x02
+#define                WIDEXFER                0x01
+
+#define        ANNEXCOL                        0x65
+
+#define        ANNEXDAT                        0x66
+
+#define        IOWNID                          0x67
+
+#define        PLL960CTL0                      0x68
+
+#define        SHCNT                           0x68
+
+#define        TOWNID                          0x69
+
+#define        PLL960CTL1                      0x69
+
+#define        PLL960CNT0                      0x6a
+
+#define        XSIG                            0x6a
+
+#define        SELOID                          0x6b
+
+#define        PLL400CTL0                      0x6c
+#define                PLL_VCOSEL              0x80
+#define                PLL_PWDN                0x40
+#define                PLL_NS                  0x30
+#define                PLL_ENLUD               0x08
+#define                PLL_ENLPF               0x04
+#define                PLL_DLPF                0x02
+#define                PLL_ENFBM               0x01
+
+#define        FAIRNESS                        0x6c
+
+#define        PLL400CTL1                      0x6d
+#define                PLL_CNTEN               0x80
+#define                PLL_CNTCLR              0x40
+#define                PLL_RST                 0x01
+
+#define        PLL400CNT0                      0x6e
+
+#define        UNFAIRNESS                      0x6e
+
+#define        HADDR                           0x70
+
+#define        HODMAADR                        0x70
+
+#define        HODMACNT                        0x78
+
+#define        HCNT                            0x78
+
+#define        HODMAEN                         0x7a
+
+#define        SGHADDR                         0x7c
+
+#define        SCBHADDR                        0x7c
+
+#define        SGHCNT                          0x84
+
+#define        SCBHCNT                         0x84
+
+#define        DFF_THRSH                       0x88
+#define                WR_DFTHRSH              0x70
+#define                WR_DFTHRSH_MAX          0x70
+#define                WR_DFTHRSH_90           0x60
+#define                WR_DFTHRSH_85           0x50
+#define                WR_DFTHRSH_75           0x40
+#define                WR_DFTHRSH_63           0x30
+#define                WR_DFTHRSH_50           0x20
+#define                WR_DFTHRSH_25           0x10
+#define                RD_DFTHRSH              0x07
+#define                RD_DFTHRSH_MAX          0x07
+#define                RD_DFTHRSH_90           0x06
+#define                RD_DFTHRSH_85           0x05
+#define                RD_DFTHRSH_75           0x04
+#define                RD_DFTHRSH_63           0x03
+#define                RD_DFTHRSH_50           0x02
+#define                RD_DFTHRSH_25           0x01
+#define                WR_DFTHRSH_MIN          0x00
+#define                RD_DFTHRSH_MIN          0x00
+
+#define        ROMADDR                         0x8a
+
+#define        ROMCNTRL                        0x8d
+#define                ROMOP                   0xe0
+#define                ROMSPD                  0x18
+#define                REPEAT                  0x02
+#define                RDY                     0x01
+
+#define        ROMDATA                         0x8e
+
+#define        CMCRXMSG0                       0x90
+
+#define        ROENABLE                        0x90
+#define                MSIROEN                 0x20
+#define                OVLYROEN                0x10
+#define                CMCROEN                 0x08
+#define                SGROEN                  0x04
+#define                DCH1ROEN                0x02
+#define                DCH0ROEN                0x01
+
+#define        OVLYRXMSG0                      0x90
+
+#define        DCHRXMSG0                       0x90
+
+#define        OVLYRXMSG1                      0x91
+
+#define        NSENABLE                        0x91
+#define                MSINSEN                 0x20
+#define                OVLYNSEN                0x10
+#define                CMCNSEN                 0x08
+#define                SGNSEN                  0x04
+#define                DCH1NSEN                0x02
+#define                DCH0NSEN                0x01
+
+#define        DCHRXMSG1                       0x91
+
+#define        CMCRXMSG1                       0x91
+
+#define        DCHRXMSG2                       0x92
+
+#define        OVLYRXMSG2                      0x92
+
+#define        CMCRXMSG2                       0x92
+
+#define        OST                             0x92
+
+#define        DCHRXMSG3                       0x93
+
+#define        CMCRXMSG3                       0x93
+
+#define        PCIXCTL                         0x93
+#define                SERRPULSE               0x80
+#define                UNEXPSCIEN              0x20
+#define                SPLTSMADIS              0x10
+#define                SPLTSTADIS              0x08
+#define                SRSPDPEEN               0x04
+#define                TSCSERREN               0x02
+#define                CMPABCDIS               0x01
+
+#define        OVLYRXMSG3                      0x93
+
+#define        OVLYSEQBCNT                     0x94
+
+#define        CMCSEQBCNT                      0x94
+
+#define        DCHSEQBCNT                      0x94
+
+#define        CMCSPLTSTAT0                    0x96
+
+#define        OVLYSPLTSTAT0                   0x96
+
+#define        DCHSPLTSTAT0                    0x96
+
+#define        DCHSPLTSTAT1                    0x97
+
+#define        CMCSPLTSTAT1                    0x97
+
+#define        OVLYSPLTSTAT1                   0x97
+
+#define        SGRXMSG0                        0x98
+#define                CDNUM                   0xf8
+#define                CFNUM                   0x07
+
+#define        SLVSPLTOUTADR0                  0x98
+#define                LOWER_ADDR              0x7f
+
+#define        SGRXMSG1                        0x99
+#define                CBNUM                   0xff
+
+#define        SLVSPLTOUTADR1                  0x99
+#define                REQ_DNUM                0xf8
+#define                REQ_FNUM                0x07
+
+#define        SGRXMSG2                        0x9a
+#define                MINDEX                  0xff
+
+#define        SLVSPLTOUTADR2                  0x9a
+#define                REQ_BNUM                0xff
+
+#define        SGRXMSG3                        0x9b
+#define                MCLASS                  0x0f
+
+#define        SLVSPLTOUTADR3                  0x9b
+#define                TAG_NUM                 0x1f
+#define                RLXORD                  0x10
+
+#define        SGSEQBCNT                       0x9c
+
+#define        SLVSPLTOUTATTR0                 0x9c
+#define                LOWER_BCNT              0xff
+
+#define        SLVSPLTOUTATTR1                 0x9d
+#define                CMPLT_DNUM              0xf8
+#define                CMPLT_FNUM              0x07
+
+#define        SLVSPLTOUTATTR2                 0x9e
+#define                CMPLT_BNUM              0xff
+
+#define        SGSPLTSTAT0                     0x9e
+#define                STAETERM                0x80
+#define                SCBCERR                 0x40
+#define                SCADERR                 0x20
+#define                SCDATBUCKET             0x10
+#define                CNTNOTCMPLT             0x08
+#define                RXOVRUN                 0x04
+#define                RXSCEMSG                0x02
+#define                RXSPLTRSP               0x01
+
+#define        SFUNCT                          0x9f
+#define                TEST_GROUP              0xf0
+#define                TEST_NUM                0x0f
+
+#define        SGSPLTSTAT1                     0x9f
+#define                RXDATABUCKET            0x01
+
+#define        DF0PCISTAT                      0xa0
+
+#define        REG0                            0xa0
+
+#define        DF1PCISTAT                      0xa1
+
+#define        SGPCISTAT                       0xa2
+
+#define        REG1                            0xa2
+
+#define        CMCPCISTAT                      0xa3
+
+#define        OVLYPCISTAT                     0xa4
+#define                SCAAPERR                0x08
+#define                RDPERR                  0x04
+
+#define        REG2                            0xa4
+
+#define        SG_STATE                        0xa6
+#define                FETCH_INPROG            0x04
+#define                LOADING_NEEDED          0x02
+#define                SEGS_AVAIL              0x01
+
+#define        MSIPCISTAT                      0xa6
+#define                RMA                     0x20
+#define                RTA                     0x10
+#define                CLRPENDMSI              0x08
+#define                DPR                     0x01
+
+#define        TARGPCISTAT                     0xa6
+#define                DPE                     0x80
+#define                SSE                     0x40
+#define                STA                     0x08
+#define                TWATERR                 0x02
+
+#define        DATA_COUNT_ODD                  0xa7
+
+#define        SCBPTR                          0xa8
+
+#define        CCSCBACNT                       0xab
+
+#define        SCBAUTOPTR                      0xab
+#define                AUSCBPTR_EN             0x80
+#define                SCBPTR_ADDR             0x38
+#define                SCBPTR_OFF              0x07
+
+#define        CCSGADDR                        0xac
+
+#define        CCSCBADDR                       0xac
+
+#define        CCSCBADR_BK                     0xac
+
+#define        CMC_RAMBIST                     0xad
+#define                SG_ELEMENT_SIZE         0x80
+#define                SCBRAMBIST_FAIL         0x40
+#define                SG_BIST_FAIL            0x20
+#define                SG_BIST_EN              0x10
+#define                CMC_BUFFER_BIST_FAIL    0x02
+#define                CMC_BUFFER_BIST_EN      0x01
+
+#define        CCSGCTL                         0xad
+#define                CCSGDONE                0x80
+#define                SG_CACHE_AVAIL          0x10
+#define                CCSGEN                  0x08
+#define                SG_FETCH_REQ            0x02
+#define                CCSGRESET               0x01
+
+#define        CCSCBCTL                        0xad
+#define                CCSCBDONE               0x80
+#define                ARRDONE                 0x40
+#define                CCARREN                 0x10
+#define                CCSCBEN                 0x08
+#define                CCSCBDIR                0x04
+#define                CCSCBRESET              0x01
+
+#define        CCSGRAM                         0xb0
+
+#define        FLEXADR                         0xb0
+
+#define        CCSCBRAM                        0xb0
+
+#define        FLEXCNT                         0xb3
+
+#define        FLEXDMASTAT                     0xb5
+#define                FLEXDMAERR              0x02
+#define                FLEXDMADONE             0x01
+
+#define        FLEXDATA                        0xb6
+
+#define        BRDDAT                          0xb8
+
+#define        BRDCTL                          0xb9
+#define                FLXARBACK               0x80
+#define                FLXARBREQ               0x40
+#define                BRDADDR                 0x38
+#define                BRDEN                   0x04
+#define                BRDRW                   0x02
+#define                BRDSTB                  0x01
+
+#define        SEEADR                          0xba
+
+#define        SEEDAT                          0xbc
+
+#define        SEECTL                          0xbe
+#define                SEEOP_ERASE             0x70
+#define                SEEOPCODE               0x70
+#define                SEEOP_READ              0x60
+#define                SEEOP_WRITE             0x50
+#define                SEEOP_EWEN              0x40
+#define                SEEOP_WALL              0x40
+#define                SEEOP_ERAL              0x40
+#define                SEEOP_EWDS              0x40
+#define                SEERST                  0x02
+#define                SEESTART                0x01
+
+#define        SEESTAT                         0xbe
+#define                INIT_DONE               0x80
+#define                LDALTID_L               0x08
+#define                SEEARBACK               0x04
+#define                SEEBUSY                 0x02
+
+#define        SCBCNT                          0xbf
+
+#define        DFWADDR                         0xc0
+
+#define        DSPFLTRCTL                      0xc0
+#define                FLTRDISABLE             0x20
+#define                EDGESENSE               0x10
+#define                DSPFCNTSEL              0x0f
+
+#define        CLRSEQINTSTAT                   0xc0
+#define                CLRSEQ_SWTMRTO          0x10
+#define                CLRSEQ_SEQINT           0x08
+#define                CLRSEQ_SCSIINT          0x04
+#define                CLRSEQ_PCIINT           0x02
+#define                CLRSEQ_SPLTINT          0x01
+
+#define        DSPDATACTL                      0xc1
+#define                BYPASSENAB              0x80
+#define                DESQDIS                 0x10
+#define                RCVROFFSTDIS            0x04
+#define                XMITOFFSTDIS            0x02
+
+#define        DFRADDR                         0xc2
+
+#define        DSPREQCTL                       0xc2
+#define                MANREQCTL               0xc0
+#define                MANREQDLY               0x3f
+
+#define        DSPACKCTL                       0xc3
+#define                MANACKCTL               0xc0
+#define                MANACKDLY               0x3f
+
+#define        DFDAT                           0xc4
+
+#define        DSPSELECT                       0xc4
+#define                AUTOINCEN               0x80
+#define                DSPSEL                  0x1f
+
+#define        WRTBIASCTL                      0xc5
+#define                AUTOXBCDIS              0x80
+#define                XMITMANVAL              0x3f
+
+#define        RCVRBIOSCTL                     0xc6
+#define                AUTORBCDIS              0x80
+#define                RCVRMANVAL              0x3f
+
+#define        WRTBIASCALC                     0xc7
+
+#define        DFPTRS                          0xc8
+
+#define        RCVRBIASCALC                    0xc8
+
+#define        DFDBCTL                         0xc8
+#define                DFF_CIO_WR_RDY          0x20
+#define                DFF_CIO_RD_RDY          0x10
+#define                DFF_DIR_ERR             0x08
+#define                DFF_RAMBIST_FAIL        0x04
+#define                DFF_RAMBIST_DONE        0x02
+#define                DFF_RAMBIST_EN          0x01
+
+#define        DFBKPTR                         0xc9
+
+#define        SKEWCALC                        0xc9
+
+#define        DFSCNT                          0xcc
+
+#define        DFBCNT                          0xce
+
+#define        OVLYADDR                        0xd4
+
+#define        SEQCTL0                         0xd6
+#define                PERRORDIS               0x80
+#define                PAUSEDIS                0x40
+#define                FAILDIS                 0x20
+#define                FASTMODE                0x10
+#define                BRKADRINTEN             0x08
+#define                STEP                    0x04
+#define                SEQRESET                0x02
+#define                LOADRAM                 0x01
+
+#define        SEQCTL1                         0xd7
+#define                OVRLAY_DATA_CHK         0x08
+#define                RAMBIST_DONE            0x04
+#define                RAMBIST_FAIL            0x02
+#define                RAMBIST_EN              0x01
+
+#define        FLAGS                           0xd8
+#define                ZERO                    0x02
+#define                CARRY                   0x01
+
+#define        SEQINTCTL                       0xd9
+#define                INTVEC1DSL              0x80
+#define                INT1_CONTEXT            0x20
+#define                SCS_SEQ_INT1M1          0x10
+#define                SCS_SEQ_INT1M0          0x08
+#define                INTMASK                 0x06
+#define                IRET                    0x01
+
+#define        SEQRAM                          0xda
+
+#define        PRGMCNT                         0xde
+
+#define        SWTIMER                         0xe0
+
+#define        ACCUM                           0xe0
+
+#define        SINDEX                          0xe2
+
+#define        DINDEX                          0xe4
+
+#define        BRKADDR1                        0xe6
+#define                BRKDIS                  0x80
+
+#define        BRKADDR0                        0xe6
+
+#define        ALLONES                         0xe8
+
+#define        ALLZEROS                        0xea
+
+#define        NONE                            0xea
+
+#define        SINDIR                          0xec
+
+#define        DINDIR                          0xed
+
+#define        FUNCTION1                       0xf0
+
+#define        STACK                           0xf2
+
+#define        CURADDR                         0xf4
+
+#define        INTVEC1_ADDR                    0xf4
+
+#define        INTVEC2_ADDR                    0xf6
+
+#define        LASTADDR                        0xf6
+
+#define        LONGJMP_ADDR                    0xf8
+
+#define        LONGJMP_SCB                     0xfa
+
+#define        ACCUM_SAVE                      0xfc
+
+#define        WAITING_SCB_TAILS               0x100
+
+#define        AHD_PCI_CONFIG_BASE             0x100
+
+#define        SRAM_BASE                       0x100
+
+#define        WAITING_TID_HEAD                0x120
+
+#define        WAITING_TID_TAIL                0x122
+
+#define        NEXT_QUEUED_SCB_ADDR            0x124
+
+#define        COMPLETE_SCB_HEAD               0x128
+
+#define        COMPLETE_SCB_DMAINPROG_HEAD             0x12a
+
+#define        COMPLETE_DMA_SCB_HEAD           0x12c
+
+#define        QFREEZE_COUNT                   0x12e
+
+#define        SAVED_MODE                      0x130
+
+#define        MSG_OUT                         0x131
+
+#define        DMAPARAMS                       0x132
+#define                PRELOADEN               0x80
+#define                WIDEODD                 0x40
+#define                SCSIEN                  0x20
+#define                SDMAEN                  0x10
+#define                SDMAENACK               0x10
+#define                HDMAENACK               0x08
+#define                HDMAEN                  0x08
+#define                DIRECTION               0x04
+#define                FIFOFLUSH               0x02
+#define                FIFORESET               0x01
+
+#define        SEQ_FLAGS                       0x133
+#define                NOT_IDENTIFIED          0x80
+#define                NO_CDB_SENT             0x40
+#define                TARGET_CMD_IS_TAGGED    0x40
+#define                DPHASE                  0x20
+#define                TARG_CMD_PENDING        0x10
+#define                CMDPHASE_PENDING        0x08
+#define                DPHASE_PENDING          0x04
+#define                SPHASE_PENDING          0x02
+#define                NO_DISCONNECT           0x01
+
+#define        SAVED_SCSIID                    0x134
+
+#define        SAVED_LUN                       0x135
+
+#define        LASTPHASE                       0x136
+#define                P_MESGIN                0xe0
+#define                PHASE_MASK              0xe0
+#define                P_STATUS                0xc0
+#define                P_MESGOUT               0xa0
+#define                CDI                     0x80
+#define                P_COMMAND               0x80
+#define                P_DATAIN_DT             0x60
+#define                P_DATAIN                0x40
+#define                IOI                     0x40
+#define                MSGI                    0x20
+#define                P_DATAOUT_DT            0x20
+#define                P_BUSFREE               0x01
+#define                P_DATAOUT               0x00
+
+#define        SHARED_DATA_ADDR                0x137
+
+#define        QOUTFIFO_NEXT_ADDR              0x13b
+
+#define        KERNEL_TQINPOS                  0x13f
+
+#define        TQINPOS                         0x140
+
+#define        ARG_1                           0x141
+#define        RETURN_1                        0x141
+#define                SEND_MSG                0x80
+#define                SEND_SENSE              0x40
+#define                SEND_REJ                0x20
+#define                MSGOUT_PHASEMIS         0x10
+#define                EXIT_MSG_LOOP           0x08
+#define                CONT_MSG_LOOP_WRITE     0x04
+#define                CONT_MSG_LOOP_READ      0x03
+#define                CONT_MSG_LOOP_TARG      0x02
+
+#define        ARG_2                           0x142
+#define        RETURN_2                        0x142
+
+#define        LAST_MSG                        0x143
+
+#define        SCSISEQ_TEMPLATE                0x144
+#define                MANUALCTL               0x40
+#define                ENSELI                  0x20
+#define                ENRSELI                 0x10
+#define                MANUALP                 0x0c
+#define                ENAUTOATNP              0x02
+#define                ALTSTIM                 0x01
+
+#define        INITIATOR_TAG                   0x145
+
+#define        SEQ_FLAGS2                      0x146
+#define                SELECTOUT_QFROZEN       0x04
+#define                TARGET_MSG_PENDING      0x02
+#define                SCB_DMA                 0x01
+
+#define        CMDSIZE_TABLE                   0x147
+
+#define        SCB_BASE                        0x180
+
+#define        SCB_RESIDUAL_DATACNT            0x180
+#define        SCB_CDB_STORE                   0x180
+
+#define        SCB_RESIDUAL_SGPTR              0x184
+#define        SCB_CDB_PTR                     0x184
+#define                SG_ADDR_MASK            0xf8
+#define                SG_OVERRUN_RESID        0x02
+
+#define        SCB_SCSI_STATUS                 0x188
+
+#define        SCB_TARGET_PHASES               0x189
+
+#define        SCB_TARGET_DATA_DIR             0x18a
+
+#define        SCB_TARGET_ITAG                 0x18b
+
+#define        SCB_SENSE_BUSADDR               0x18c
+#define        SCB_NEXT_COMPLETE               0x18c
+
+#define        SCB_CDB_LEN                     0x190
+#define                SCB_CDB_LEN_PTR         0x80
+
+#define        SCB_TASK_MANAGEMENT             0x191
+
+#define        SCB_TAG                         0x192
+
+#define        SCB_NEXT                        0x194
+#define        SCB_NEXT_SCB_BUSADDR            0x194
+
+#define        SCB_NEXT2                       0x196
+
+#define        SCB_DATAPTR                     0x198
+
+#define        SCB_DATACNT                     0x1a0
+#define                SG_LAST_SEG             0x80
+#define                SG_HIGH_ADDR_BITS       0x7f
+
+#define        SCB_SGPTR                       0x1a4
+#define                SG_STATUS_VALID         0x04
+#define                SG_FULL_RESID           0x02
+#define                SG_LIST_NULL            0x01
+
+#define        SCB_CONTROL                     0x1a8
+#define                TARGET_SCB              0x80
+#define                DISCENB                 0x40
+#define                TAG_ENB                 0x20
+#define                MK_MESSAGE              0x10
+#define                STATUS_RCVD             0x08
+#define                DISCONNECTED            0x04
+#define                SCB_TAG_TYPE            0x03
+
+#define        SCB_SCSIID                      0x1a9
+#define                TID                     0xf0
+#define                OID                     0x0f
+
+#define        SCB_LUN                         0x1aa
+#define                LID                     0xff
+
+#define        SCB_TASK_ATTRIBUTE              0x1ab
+#define        SCB_NONPACKET_TAG               0x1ab
+
+#define        SCB_BUSADDR                     0x1ac
+
+#define        SCB_DISCONNECTED_LISTS          0x1b0
+
+
+#define        SCB_TRANSFER_SIZE       0x30
+#define        STATUS_QUEUE_FULL       0x28
+#define        STATUS_BUSY     0x08
+#define        MAX_OFFSET_PACED        0x7f
+#define        BUS_32_BIT      0x02
+#define        CCSGADDR_MAX    0x80
+#define        TID_SHIFT       0x04
+#define        MK_MESSAGE_BIT_OFFSET   0x04
+#define        SEEOP_EWDS_ADDR 0x00
+#define        AHD_NUM_ANNEXCOLS       0x04
+#define        AHD_PRECOMP_FASTSLEW    0x40
+#define        AHD_PRECOMP_CUTBACK_29  0x06
+#define        NVRAM_SCB_OFFSET        0x2c
+#define        STATUS_PKT_SENSE        0xff
+#define        CMD_GROUP_CODE_SHIFT    0x05
+#define        AHD_SENSE_BUFSIZE       0x100
+#define        BUS_8_BIT       0x00
+#define        STIMESEL_BUG_ADJ        0x08
+#define        STIMESEL_MIN    0x18
+#define        STIMESEL_SHIFT  0x03
+#define        CCSGRAM_MAXSEGS 0x10
+#define        INVALID_ADDR    0x80
+#define        TARGET_CMD_CMPLT        0xfe
+#define        SEEOP_WRAL_ADDR 0x40
+#define        SEEOP_ERAL_ADDR 0x80
+#define        AHD_PRECOMP_CUTBACK_37  0x07
+#define        AHD_PRECOMP_CUTBACK_17  0x04
+#define        AHD_PRECOMP_MASK        0x07
+#define        AHD_ANNEXCOL_PRECOMP    0x04
+#define        SRC_MODE_SHIFT  0x00
+#define        PKT_OVERRUN_BUFSIZE     0x200
+#define        TARGET_DATA_IN  0x01
+#define        HOST_MSG        0xff
+#define        MAX_OFFSET      0xfe
+#define        BUS_16_BIT      0x01
+#define        CCSCBADDR_MAX   0x80
+#define        SEQ_STACK_SIZE  0x08
+#define        WRTBIASCTL_CPQ_DEFAULT  0x97
+#define        NUMDSPS         0x14
+#define        SEEOP_EWEN_ADDR 0xc0
+#define        DST_MODE_SHIFT  0x04
+
+
+/* Downloaded Constant Definitions */
+#define        PKT_OVERRUN_BUFOFFSET   0x05
+#define        SG_SIZEOF       0x04
+#define        SG_PREFETCH_ADDR_MASK   0x03
+#define        SG_PREFETCH_ALIGN_MASK  0x02
+#define        SG_PREFETCH_CNT_LIMIT   0x01
+#define        SG_PREFETCH_CNT 0x00
+#define        DOWNLOAD_CONST_COUNT    0x06
+
+
+/* Exported Labels */
+#define        LABEL_seq_isr   0x215
diff --git a/xen/drivers/scsi/aic7xxx/aic79xx_seq.h b/xen/drivers/scsi/aic7xxx/aic79xx_seq.h
new file mode 100644 (file)
index 0000000..eafb47e
--- /dev/null
@@ -0,0 +1,824 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ *              from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#42 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#34 $
+ */
+static uint8_t seqprog[] = {
+       0x33, 0xea, 0xda, 0x58,
+       0x60, 0x3a, 0x0c, 0x68,
+       0x04, 0x46, 0x0d, 0x68,
+       0xff, 0x21, 0x0d, 0x70,
+       0x40, 0x4b, 0x16, 0x69,
+       0x00, 0xe2, 0xe0, 0x58,
+       0x40, 0x4b, 0x16, 0x69,
+       0x20, 0x4b, 0x0a, 0x69,
+       0xfc, 0x42, 0x16, 0x78,
+       0x10, 0x40, 0x16, 0x78,
+       0x00, 0xe2, 0xf4, 0x5c,
+       0x20, 0x4d, 0x1a, 0x78,
+       0x00, 0xe2, 0xf4, 0x5c,
+       0x01, 0x52, 0x3c, 0x78,
+       0x02, 0x58, 0x50, 0x31,
+       0xff, 0xea, 0x10, 0x0b,
+       0x08, 0xa8, 0x51, 0x03,
+       0x01, 0xa4, 0x29, 0x78,
+       0x00, 0xe2, 0xc6, 0x5a,
+       0x00, 0xe2, 0x1a, 0x40,
+       0xff, 0xea, 0xd4, 0x19,
+       0x02, 0xa8, 0x82, 0x32,
+       0x00, 0xea, 0xda, 0x58,
+       0x00, 0xe2, 0xe8, 0x5c,
+       0x00, 0xe2, 0x3c, 0x48,
+       0x11, 0xea, 0xda, 0x58,
+       0x00, 0xe2, 0xe8, 0x5c,
+       0x00, 0xe2, 0x3c, 0x48,
+       0x33, 0xea, 0xda, 0x58,
+       0x00, 0xe2, 0xbe, 0x5a,
+       0x00, 0xea, 0xda, 0x58,
+       0x80, 0xf9, 0x42, 0x68,
+       0x00, 0xe2, 0xd8, 0x58,
+       0x11, 0xea, 0xda, 0x58,
+       0x80, 0xf9, 0x48, 0x68,
+       0x00, 0xe2, 0xd8, 0x58,
+       0x00, 0xe2, 0x4c, 0x58,
+       0x00, 0xe2, 0x00, 0x40,
+       0x22, 0xea, 0xda, 0x58,
+       0x18, 0xad, 0x94, 0x78,
+       0x04, 0xad, 0x64, 0x68,
+       0x80, 0xad, 0xa0, 0x7c,
+       0x10, 0xad, 0x58, 0x78,
+       0xe7, 0xad, 0x5a, 0x0d,
+       0xe7, 0xad, 0x5a, 0x09,
+       0x02, 0xea, 0x02, 0x00,
+       0xff, 0xea, 0x56, 0x02,
+       0x04, 0x7c, 0x76, 0x32,
+       0x20, 0x16, 0xa0, 0x7c,
+       0x04, 0x37, 0x77, 0x36,
+       0x40, 0xad, 0xa0, 0x7c,
+       0xe7, 0xad, 0x5a, 0x09,
+       0x02, 0xa8, 0x40, 0x31,
+       0x04, 0x94, 0x49, 0x32,
+       0xff, 0xea, 0x2a, 0x03,
+       0xff, 0xea, 0x2e, 0x03,
+       0x10, 0xa8, 0x85, 0x68,
+       0x3d, 0xa9, 0xc5, 0x29,
+       0xfe, 0xe2, 0xc4, 0x09,
+       0x01, 0xea, 0xc6, 0x01,
+       0x02, 0xe2, 0xc8, 0x31,
+       0x02, 0xec, 0x50, 0x31,
+       0x02, 0xa0, 0xda, 0x31,
+       0xff, 0xa9, 0x84, 0x70,
+       0x02, 0xa0, 0x28, 0x33,
+       0x01, 0x10, 0xd4, 0x35,
+       0xff, 0x21, 0x8f, 0x70,
+       0x02, 0x22, 0x51, 0x31,
+       0x02, 0xa0, 0x2c, 0x33,
+       0x02, 0xa0, 0x44, 0x32,
+       0x01, 0x10, 0xd4, 0x35,
+       0x02, 0xa0, 0x40, 0x32,
+       0x02, 0xa0, 0x44, 0x32,
+       0x01, 0x10, 0xd4, 0x35,
+       0x04, 0x46, 0x99, 0x68,
+       0x40, 0x16, 0xbc, 0x68,
+       0xff, 0x2d, 0xc1, 0x60,
+       0xff, 0x29, 0xa1, 0x74,
+       0x02, 0x28, 0x55, 0x32,
+       0x01, 0xea, 0x5a, 0x01,
+       0x04, 0x3b, 0xf9, 0x30,
+       0x02, 0x28, 0x61, 0x31,
+       0x02, 0x28, 0x51, 0x31,
+       0x00, 0xe2, 0xac, 0x40,
+       0x02, 0x8c, 0x61, 0x31,
+       0x02, 0x8c, 0x51, 0x31,
+       0x01, 0x14, 0xd4, 0x31,
+       0xff, 0x8d, 0xb5, 0x70,
+       0x80, 0xac, 0xb4, 0x70,
+       0x20, 0x16, 0xa8, 0x78,
+       0x01, 0xac, 0x08, 0x31,
+       0x09, 0xea, 0x5a, 0x01,
+       0x02, 0x8c, 0x51, 0x32,
+       0xff, 0xea, 0x1a, 0x07,
+       0x04, 0x24, 0xf9, 0x30,
+       0x1d, 0xea, 0xcc, 0x40,
+       0x02, 0x2c, 0x51, 0x31,
+       0x04, 0xac, 0xf9, 0x30,
+       0x19, 0xea, 0xcc, 0x58,
+       0x02, 0x8c, 0x59, 0x32,
+       0x02, 0x28, 0x19, 0x33,
+       0x02, 0xa8, 0x50, 0x36,
+       0x30, 0xea, 0x08, 0x01,
+       0x01, 0xe2, 0x5a, 0x31,
+       0x01, 0x46, 0x8d, 0x06,
+       0x02, 0xa8, 0xf4, 0x31,
+       0x02, 0xf2, 0xf0, 0x35,
+       0x02, 0xf2, 0xf0, 0x31,
+       0x02, 0xf8, 0xe4, 0x35,
+       0x80, 0xea, 0xb2, 0x01,
+       0x01, 0xe2, 0x00, 0x30,
+       0xff, 0xea, 0xb2, 0x0d,
+       0x02, 0x20, 0xbd, 0x30,
+       0x02, 0x20, 0xb9, 0x30,
+       0x02, 0x20, 0x51, 0x31,
+       0x4c, 0xa9, 0xd7, 0x28,
+       0x10, 0xa8, 0xef, 0x78,
+       0x01, 0x6b, 0xc0, 0x30,
+       0x02, 0x64, 0xc8, 0x00,
+       0x40, 0x3a, 0x74, 0x04,
+       0x00, 0xea, 0xda, 0x58,
+       0x80, 0xf9, 0xf6, 0x68,
+       0x00, 0xe2, 0xd8, 0x58,
+       0x11, 0xea, 0xda, 0x58,
+       0x80, 0xf9, 0xfc, 0x68,
+       0x00, 0xe2, 0xd8, 0x58,
+       0x33, 0xea, 0xda, 0x58,
+       0x30, 0x3f, 0xc0, 0x09,
+       0x30, 0xe0, 0xf0, 0x60,
+       0x02, 0xa8, 0x82, 0x32,
+       0x01, 0x3f, 0x7e, 0x00,
+       0x11, 0xea, 0xda, 0x58,
+       0x02, 0x41, 0x51, 0x35,
+       0x08, 0x3c, 0x78, 0x00,
+       0xf0, 0x49, 0x68, 0x0a,
+       0x0f, 0x67, 0xc0, 0x09,
+       0x00, 0x34, 0x69, 0x02,
+       0x20, 0xea, 0x96, 0x00,
+       0x00, 0xe2, 0x70, 0x41,
+       0x40, 0x3a, 0x32, 0x69,
+       0x02, 0x55, 0x00, 0x68,
+       0x02, 0x56, 0x32, 0x69,
+       0xff, 0x5b, 0x32, 0x61,
+       0x02, 0x20, 0x51, 0x31,
+       0x80, 0xea, 0xb2, 0x01,
+       0x44, 0xea, 0x00, 0x00,
+       0x01, 0x33, 0xc0, 0x31,
+       0x33, 0xea, 0x00, 0x00,
+       0xff, 0xea, 0xb2, 0x09,
+       0xff, 0xe0, 0xc0, 0x19,
+       0xff, 0xe0, 0x34, 0x79,
+       0x02, 0x94, 0x51, 0x31,
+       0x00, 0xe2, 0x2a, 0x41,
+       0x02, 0x5e, 0x50, 0x31,
+       0x02, 0xa8, 0xb8, 0x30,
+       0x02, 0x5c, 0x50, 0x31,
+       0xff, 0x95, 0x45, 0x71,
+       0x02, 0x94, 0x41, 0x31,
+       0x02, 0x22, 0x51, 0x31,
+       0x02, 0xa0, 0x2c, 0x33,
+       0x02, 0xa0, 0x44, 0x32,
+       0x00, 0xe2, 0x4e, 0x41,
+       0x10, 0xa8, 0x4f, 0x69,
+       0x3d, 0xa9, 0xc9, 0x29,
+       0x01, 0xe4, 0xc8, 0x01,
+       0x01, 0xea, 0xca, 0x01,
+       0xff, 0xea, 0xda, 0x01,
+       0x02, 0x20, 0x51, 0x31,
+       0x02, 0x96, 0x41, 0x32,
+       0xff, 0x21, 0x57, 0x61,
+       0xff, 0xea, 0x46, 0x02,
+       0x02, 0x5c, 0x50, 0x31,
+       0x40, 0xea, 0x96, 0x00,
+       0x02, 0x56, 0xfc, 0x6c,
+       0x01, 0x55, 0xfc, 0x6c,
+       0x10, 0xa8, 0x63, 0x79,
+       0x10, 0x40, 0x64, 0x69,
+       0x01, 0x56, 0x00, 0x68,
+       0xbf, 0x3a, 0x74, 0x08,
+       0x01, 0xa9, 0x69, 0x32,
+       0x01, 0xaa, 0x6b, 0x32,
+       0x08, 0x3c, 0x78, 0x00,
+       0x80, 0xea, 0x62, 0x02,
+       0x40, 0xea, 0x66, 0x02,
+       0x00, 0xe2, 0x2a, 0x5b,
+       0x01, 0x36, 0xc1, 0x31,
+       0x9f, 0xe0, 0xc6, 0x7b,
+       0x80, 0xe0, 0x82, 0x71,
+       0xa0, 0xe0, 0xb4, 0x71,
+       0xc0, 0xe0, 0xa6, 0x71,
+       0xe0, 0xe0, 0xe4, 0x71,
+       0x01, 0xea, 0x04, 0x00,
+       0x00, 0xe2, 0x70, 0x41,
+       0xee, 0x00, 0x86, 0x69,
+       0x05, 0xea, 0xb4, 0x00,
+       0x00, 0xe2, 0xfe, 0x58,
+       0xef, 0x90, 0xd5, 0x19,
+       0x00, 0xe2, 0x94, 0x51,
+       0x0b, 0x84, 0xe1, 0x30,
+       0x02, 0xea, 0x36, 0x00,
+       0xa8, 0xea, 0x32, 0x00,
+       0x00, 0xe2, 0x9c, 0x41,
+       0x01, 0x90, 0xd1, 0x30,
+       0x02, 0xea, 0xd2, 0x30,
+       0x10, 0x80, 0x89, 0x31,
+       0x20, 0xea, 0x32, 0x00,
+       0x20, 0x19, 0x9c, 0x69,
+       0xbf, 0x33, 0x67, 0x0a,
+       0x02, 0x4d, 0x70, 0x69,
+       0x40, 0x33, 0x67, 0x02,
+       0x00, 0xe2, 0x70, 0x41,
+       0x80, 0x33, 0xad, 0x79,
+       0x03, 0xea, 0x04, 0x00,
+       0x00, 0xe2, 0x16, 0x42,
+       0x01, 0x44, 0x10, 0x33,
+       0x08, 0xa8, 0x51, 0x03,
+       0x00, 0xe2, 0x70, 0x41,
+       0x10, 0xea, 0x80, 0x00,
+       0x01, 0x31, 0xc5, 0x31,
+       0x80, 0xe2, 0xd0, 0x61,
+       0x10, 0xa8, 0xf5, 0x69,
+       0xc0, 0xaa, 0xc5, 0x01,
+       0x40, 0xa8, 0xc1, 0x69,
+       0xbf, 0xe2, 0xc4, 0x09,
+       0x20, 0xa8, 0xd5, 0x79,
+       0x01, 0xe2, 0x88, 0x30,
+       0x00, 0xe2, 0x2a, 0x5b,
+       0xa0, 0x36, 0xdd, 0x61,
+       0x23, 0xa8, 0x89, 0x08,
+       0x00, 0xe2, 0x2a, 0x5b,
+       0xa0, 0x36, 0xdd, 0x61,
+       0x00, 0xab, 0xd5, 0x41,
+       0xff, 0xe2, 0xd4, 0x61,
+       0x00, 0xe2, 0xf4, 0x41,
+       0x40, 0xea, 0x98, 0x00,
+       0x01, 0xe2, 0x88, 0x30,
+       0x00, 0xe2, 0x2a, 0x5b,
+       0xa0, 0x36, 0xb3, 0x71,
+       0x40, 0xea, 0x98, 0x00,
+       0x01, 0x31, 0x87, 0x32,
+       0x08, 0xea, 0x62, 0x02,
+       0x00, 0xe2, 0x70, 0x41,
+       0xe0, 0xea, 0x36, 0x5b,
+       0x80, 0xe0, 0x1a, 0x6a,
+       0x04, 0xe0, 0xd4, 0x72,
+       0x02, 0xe0, 0x0c, 0x73,
+       0x00, 0xea, 0x9a, 0x72,
+       0x03, 0xe0, 0x1c, 0x73,
+       0x23, 0xe0, 0x04, 0x72,
+       0x08, 0xe0, 0x16, 0x72,
+       0x00, 0xe2, 0x2a, 0x5b,
+       0x07, 0xea, 0x04, 0x00,
+       0x08, 0x41, 0x71, 0x71,
+       0x04, 0x41, 0x01, 0x62,
+       0x01, 0x42, 0x89, 0x30,
+       0x00, 0xe2, 0xf4, 0x41,
+       0x01, 0x44, 0xd4, 0x31,
+       0x00, 0xe2, 0xf4, 0x41,
+       0x4c, 0x34, 0xc1, 0x28,
+       0x01, 0x64, 0x14, 0x7a,
+       0xa0, 0xea, 0x2e, 0x5b,
+       0x01, 0xa0, 0x14, 0x62,
+       0x01, 0x84, 0x11, 0x7a,
+       0x01, 0xa7, 0x16, 0x7a,
+       0x05, 0xea, 0x04, 0x00,
+       0x00, 0xe2, 0x16, 0x42,
+       0x07, 0xea, 0x3e, 0x5b,
+       0x01, 0x44, 0xd4, 0x31,
+       0x00, 0xe2, 0x70, 0x41,
+       0x4c, 0x34, 0xc5, 0x29,
+       0x3f, 0xe0, 0x6a, 0x0a,
+       0x01, 0x35, 0x51, 0x31,
+       0x3d, 0xe2, 0x52, 0x29,
+       0x07, 0xe2, 0xc4, 0x09,
+       0x11, 0xe2, 0xc4, 0x29,
+       0xb0, 0xe2, 0xc4, 0x19,
+       0x01, 0xea, 0xc6, 0x01,
+       0x02, 0xe2, 0xc8, 0x31,
+       0x02, 0xec, 0x50, 0x31,
+       0xff, 0xa9, 0x36, 0x72,
+       0x20, 0xa8, 0x37, 0x6a,
+       0x04, 0x92, 0x45, 0x31,
+       0x00, 0xe2, 0x70, 0x42,
+       0x80, 0x33, 0x67, 0x02,
+       0x01, 0x44, 0xd4, 0x31,
+       0x00, 0xe2, 0x2a, 0x5b,
+       0x01, 0x33, 0x67, 0x02,
+       0xe0, 0x36, 0x97, 0x62,
+       0x02, 0x33, 0x67, 0x02,
+       0x20, 0x46, 0x92, 0x62,
+       0x02, 0xa8, 0x40, 0x31,
+       0xff, 0xea, 0x52, 0x09,
+       0xa8, 0xea, 0x2e, 0x5b,
+       0x01, 0xa8, 0x44, 0x31,
+       0x01, 0xa2, 0xc0, 0x31,
+       0x00, 0xab, 0x59, 0x62,
+       0x01, 0x34, 0xc1, 0x31,
+       0x00, 0xa9, 0x59, 0x62,
+       0x01, 0x35, 0xc1, 0x31,
+       0x00, 0xaa, 0x81, 0x72,
+       0x01, 0xa9, 0x52, 0x11,
+       0xff, 0xa9, 0x4c, 0x6a,
+       0x02, 0xa0, 0x50, 0x31,
+       0xff, 0xea, 0x42, 0x01,
+       0x01, 0xa2, 0xc0, 0x31,
+       0x00, 0xab, 0x6d, 0x72,
+       0x02, 0xa8, 0x40, 0x31,
+       0x02, 0x94, 0x51, 0x31,
+       0xff, 0xa9, 0x62, 0x62,
+       0x00, 0xe2, 0x92, 0x42,
+       0x04, 0x92, 0x45, 0x31,
+       0xff, 0xa1, 0x7a, 0x62,
+       0x4c, 0x34, 0xc5, 0x29,
+       0x01, 0x35, 0x51, 0x31,
+       0x3d, 0xe2, 0x52, 0x29,
+       0x02, 0xa4, 0xda, 0x31,
+       0x00, 0xe2, 0x7e, 0x42,
+       0x02, 0xa0, 0x50, 0x31,
+       0x02, 0xa4, 0x28, 0x33,
+       0x02, 0xa2, 0x50, 0x31,
+       0x10, 0x33, 0x67, 0x02,
+       0x04, 0xa8, 0x93, 0x7a,
+       0xfb, 0xa8, 0x51, 0x0b,
+       0xff, 0xea, 0x66, 0x0a,
+       0x01, 0xa4, 0x8d, 0x6a,
+       0x00, 0xe2, 0xfe, 0x58,
+       0x10, 0xa8, 0x17, 0x7a,
+       0xff, 0xea, 0x3e, 0x5b,
+       0x00, 0xe2, 0x16, 0x42,
+       0x04, 0xea, 0x04, 0x00,
+       0x00, 0xe2, 0x16, 0x42,
+       0x04, 0xea, 0x04, 0x00,
+       0x00, 0xe2, 0x70, 0x41,
+       0xc0, 0x33, 0xa1, 0x7a,
+       0x03, 0xea, 0x04, 0x00,
+       0x00, 0xe2, 0x16, 0x42,
+       0x08, 0xa8, 0x9d, 0x7a,
+       0x10, 0xa8, 0xa7, 0x7a,
+       0x0a, 0xea, 0x04, 0x00,
+       0x00, 0xe2, 0xbe, 0x5a,
+       0x00, 0xe2, 0xfc, 0x42,
+       0x40, 0x4b, 0xae, 0x6a,
+       0xbf, 0x3a, 0x74, 0x08,
+       0x01, 0xe0, 0xf8, 0x31,
+       0xff, 0xea, 0xc0, 0x09,
+       0x01, 0x2e, 0x5d, 0x1a,
+       0x00, 0x2f, 0x5f, 0x22,
+       0x04, 0x46, 0x8d, 0x02,
+       0x01, 0xfc, 0xc0, 0x35,
+       0x33, 0xea, 0xda, 0x58,
+       0x02, 0x41, 0x51, 0x31,
+       0xff, 0x88, 0xcb, 0x6a,
+       0x01, 0xa4, 0xc7, 0x6a,
+       0x02, 0xa4, 0xcf, 0x6a,
+       0x01, 0x84, 0xcf, 0x7a,
+       0x02, 0x28, 0x19, 0x33,
+       0x02, 0xa8, 0x50, 0x36,
+       0xff, 0x88, 0xcf, 0x72,
+       0x00, 0xe2, 0xaa, 0x5a,
+       0x02, 0x2c, 0x19, 0x33,
+       0x02, 0xa8, 0x58, 0x32,
+       0x04, 0xa4, 0x49, 0x07,
+       0xc0, 0x33, 0xdb, 0x7a,
+       0x03, 0xea, 0x04, 0x00,
+       0x00, 0xe2, 0x16, 0x42,
+       0x04, 0xa8, 0x51, 0x03,
+       0x20, 0xa8, 0xe3, 0x7a,
+       0x01, 0xab, 0xc1, 0x31,
+       0x00, 0xa8, 0xfc, 0x72,
+       0x02, 0xa8, 0x40, 0x31,
+       0x4c, 0xa9, 0xc5, 0x29,
+       0x01, 0xaa, 0x51, 0x31,
+       0x3d, 0xe2, 0x52, 0x29,
+       0x07, 0xe2, 0xc4, 0x09,
+       0x11, 0xe2, 0xc4, 0x29,
+       0xb0, 0xe2, 0xc4, 0x19,
+       0x01, 0xea, 0xc6, 0x01,
+       0x02, 0xe2, 0xc8, 0x31,
+       0x02, 0xec, 0x44, 0x31,
+       0x02, 0xa0, 0xda, 0x31,
+       0x02, 0xa0, 0x50, 0x31,
+       0x02, 0xa2, 0x28, 0x33,
+       0xf7, 0x57, 0xae, 0x08,
+       0x01, 0x44, 0xd4, 0x31,
+       0x00, 0xe2, 0x26, 0x5b,
+       0xee, 0x00, 0x06, 0x6b,
+       0x02, 0xea, 0xb4, 0x00,
+       0x09, 0x4c, 0x06, 0x7b,
+       0x08, 0x4c, 0x00, 0x68,
+       0x0b, 0xea, 0x04, 0x00,
+       0x01, 0x44, 0xd4, 0x31,
+       0x20, 0x33, 0x71, 0x79,
+       0x00, 0xe2, 0x14, 0x5b,
+       0x00, 0xe2, 0x70, 0x41,
+       0x01, 0x84, 0x19, 0x7b,
+       0x01, 0xa4, 0x49, 0x07,
+       0x08, 0x60, 0x30, 0x33,
+       0x08, 0x80, 0x41, 0x37,
+       0xdf, 0x33, 0x67, 0x0a,
+       0xee, 0x00, 0x22, 0x6b,
+       0x05, 0xea, 0xb4, 0x00,
+       0x00, 0xe2, 0xfe, 0x58,
+       0x00, 0xe2, 0x16, 0x42,
+       0x01, 0xea, 0x6c, 0x02,
+       0xc0, 0xea, 0x66, 0x06,
+       0xff, 0x42, 0x2a, 0x7b,
+       0xe0, 0x41, 0x6c, 0x0e,
+       0x01, 0x44, 0xd4, 0x31,
+       0xff, 0x42, 0x30, 0x7b,
+       0xe0, 0x41, 0x6c, 0x0a,
+       0xe0, 0x36, 0x71, 0x61,
+       0xff, 0xea, 0xca, 0x09,
+       0x01, 0xe2, 0xc8, 0x31,
+       0x01, 0x46, 0xda, 0x35,
+       0x01, 0x44, 0xd4, 0x35,
+       0x10, 0xea, 0x80, 0x00,
+       0x01, 0xe2, 0x62, 0x36,
+       0x04, 0xa6, 0xa0, 0x7c,
+       0xff, 0xea, 0x4c, 0x09,
+       0xff, 0xea, 0x5a, 0x0d,
+       0x80, 0xea, 0xb2, 0x01,
+       0x11, 0x00, 0x00, 0x10,
+       0xff, 0xea, 0xb2, 0x0d,
+       0x01, 0x00, 0x60, 0x32,
+       0x02, 0xa6, 0x54, 0x7b,
+       0x00, 0xe2, 0x5e, 0x5b,
+       0x00, 0xe2, 0x48, 0x5b,
+       0x02, 0xa6, 0x5a, 0x7b,
+       0x00, 0xe2, 0x5e, 0x5b,
+       0x00, 0xe2, 0x4c, 0x58,
+       0x00, 0x30, 0xdb, 0x40,
+       0x01, 0xa6, 0x78, 0x6b,
+       0x10, 0xad, 0xa0, 0x7c,
+       0x98, 0xad, 0x70, 0x73,
+       0x08, 0xad, 0xa0, 0x6c,
+       0x00, 0xea, 0x08, 0x81,
+       0x02, 0x84, 0xf9, 0x88,
+       0x03, 0x85, 0xfb, 0x30,
+       0x19, 0xea, 0x5a, 0x01,
+       0x04, 0xa6, 0x4c, 0x05,
+       0x04, 0xa6, 0xa0, 0x7c,
+       0x00, 0xe2, 0x46, 0x5b,
+       0x03, 0x84, 0x59, 0x89,
+       0x03, 0xea, 0x4c, 0x01,
+       0x80, 0x1a, 0xa0, 0x7c,
+       0x08, 0xb0, 0xe0, 0x30,
+       0x04, 0xb0, 0xe0, 0x30,
+       0x03, 0xb0, 0xf0, 0x30,
+       0x01, 0x78, 0x84, 0x7b,
+       0x01, 0xa7, 0x4e, 0x11,
+       0x01, 0xb0, 0x06, 0x33,
+       0x7f, 0x83, 0xe9, 0x08,
+       0x04, 0xac, 0x58, 0x19,
+       0xff, 0xea, 0xc0, 0x09,
+       0x04, 0x84, 0x09, 0x9b,
+       0x00, 0x85, 0x0b, 0x23,
+       0x00, 0x86, 0x0d, 0x23,
+       0x00, 0x87, 0x0f, 0x23,
+       0x01, 0x84, 0xc5, 0x31,
+       0x01, 0xa7, 0x9a, 0x7b,
+       0x04, 0xe2, 0xc4, 0x01,
+       0x80, 0x83, 0xa1, 0x7b,
+       0x02, 0xe2, 0xc4, 0x01,
+       0xff, 0xea, 0x4c, 0x09,
+       0x01, 0xe2, 0x36, 0x30,
+       0x88, 0x19, 0x32, 0x00,
+       0x01, 0xac, 0xd4, 0x99,
+       0x00, 0xe2, 0xa0, 0x54,
+       0xfe, 0xa6, 0x4c, 0x0d,
+       0x0b, 0x98, 0xe1, 0x30,
+       0x01, 0xa0, 0x4f, 0x09,
+       0xfd, 0xa4, 0x41, 0x09,
+       0x80, 0xa3, 0xb5, 0x7b,
+       0x02, 0xa0, 0x40, 0x01,
+       0x01, 0xa7, 0xb8, 0x7b,
+       0x04, 0xa0, 0x40, 0x01,
+       0x01, 0xa0, 0x36, 0x30,
+       0xa8, 0xea, 0x32, 0x00,
+       0xfd, 0xa4, 0x49, 0x0b,
+       0x05, 0xa3, 0x07, 0x33,
+       0x80, 0x83, 0xc5, 0x6b,
+       0x02, 0xea, 0x4c, 0x05,
+       0xff, 0xea, 0x4c, 0x0d,
+       0xc0, 0x33, 0xcb, 0x7b,
+       0x03, 0xea, 0x04, 0x00,
+       0x20, 0x33, 0xe3, 0x7b,
+       0x01, 0x84, 0xd1, 0x6b,
+       0x06, 0xea, 0x04, 0x00,
+       0xee, 0x00, 0xd6, 0x6b,
+       0x05, 0xea, 0xb4, 0x00,
+       0x33, 0xea, 0xda, 0x58,
+       0x80, 0x3d, 0x7a, 0x00,
+       0x03, 0x42, 0xd8, 0x6b,
+       0x7f, 0x3d, 0x7a, 0x08,
+       0x11, 0xea, 0xda, 0x58,
+       0x09, 0xea, 0x04, 0x00,
+       0x00, 0xe2, 0x70, 0x41,
+       0x01, 0xa4, 0xd1, 0x6b,
+       0x00, 0xe2, 0xaa, 0x5b,
+       0x20, 0x33, 0x67, 0x02,
+       0x00, 0xe2, 0x4e, 0x5b,
+       0x20, 0x19, 0xe8, 0x6b,
+       0x00, 0xe2, 0x06, 0x5c,
+       0x04, 0x19, 0xfa, 0x6b,
+       0x02, 0x19, 0x32, 0x00,
+       0x01, 0x84, 0xfb, 0x7b,
+       0x01, 0x1b, 0xf4, 0x7b,
+       0x01, 0x1a, 0xfa, 0x6b,
+       0x00, 0xe2, 0xc6, 0x43,
+       0x80, 0x4b, 0x00, 0x6c,
+       0x01, 0x4c, 0xfc, 0x7b,
+       0x03, 0x42, 0xc6, 0x6b,
+       0x00, 0xe2, 0x42, 0x5b,
+       0x04, 0x33, 0x71, 0x79,
+       0x00, 0xe2, 0x70, 0x41,
+       0x02, 0x1b, 0x0e, 0x7c,
+       0x08, 0x5d, 0x0c, 0x7c,
+       0x03, 0x68, 0x00, 0x37,
+       0x01, 0x84, 0x09, 0x07,
+       0x08, 0x5d, 0x14, 0x6c,
+       0x00, 0xe2, 0x4e, 0x5b,
+       0x00, 0xe2, 0x06, 0x44,
+       0x80, 0x1b, 0x1e, 0x7c,
+       0x80, 0x84, 0x1f, 0x6c,
+       0xff, 0x85, 0x0b, 0x1b,
+       0xff, 0x86, 0x0d, 0x23,
+       0xff, 0x87, 0x0f, 0x23,
+       0xf8, 0x1b, 0x08, 0x0b,
+       0xff, 0xea, 0x4e, 0x09,
+       0x04, 0x1b, 0x26, 0x7c,
+       0x01, 0xa7, 0x4e, 0x01,
+       0xff, 0xea, 0x06, 0x0b,
+       0x03, 0x68, 0x00, 0x37,
+       0xff, 0xea, 0xd4, 0x09,
+       0x20, 0x5b, 0x36, 0x6c,
+       0x10, 0x5b, 0x40, 0x6c,
+       0x08, 0x5b, 0x48, 0x6c,
+       0x02, 0x5b, 0x44, 0x6d,
+       0x12, 0xea, 0x04, 0x00,
+       0x08, 0x19, 0x3c, 0x7c,
+       0xdf, 0x5c, 0xb8, 0x08,
+       0x01, 0xd9, 0xb2, 0x05,
+       0x02, 0xea, 0xb4, 0x00,
+       0x01, 0xd9, 0xb2, 0x05,
+       0x01, 0xa4, 0x0f, 0x6d,
+       0x00, 0xe2, 0xaa, 0x5b,
+       0x00, 0xe2, 0x60, 0x5c,
+       0x01, 0xd9, 0xb2, 0x05,
+       0x00, 0xe2, 0xaa, 0x5a,
+       0xf3, 0x90, 0xd5, 0x19,
+       0x00, 0xe2, 0x54, 0x54,
+       0x80, 0x90, 0x55, 0x6c,
+       0x13, 0xea, 0x04, 0x00,
+       0x00, 0xe2, 0x5c, 0x44,
+       0x04, 0x8c, 0xe1, 0x30,
+       0x01, 0xea, 0xf2, 0x00,
+       0x02, 0xea, 0x36, 0x00,
+       0xa8, 0xea, 0x32, 0x00,
+       0x00, 0xe2, 0xd8, 0x5c,
+       0x01, 0xd9, 0xb2, 0x05,
+       0x02, 0xa8, 0xf4, 0x31,
+       0x02, 0xa6, 0x72, 0x7c,
+       0x00, 0xe2, 0xd4, 0x58,
+       0x20, 0x5b, 0xba, 0x6c,
+       0xfc, 0x42, 0x6c, 0x7c,
+       0x10, 0x40, 0x6e, 0x6c,
+       0x20, 0x4d, 0x70, 0x7c,
+       0x08, 0x5d, 0xba, 0x6c,
+       0x02, 0xa6, 0x5e, 0x6b,
+       0x00, 0xe2, 0xd4, 0x58,
+       0x20, 0x5b, 0xba, 0x6c,
+       0x01, 0x1b, 0x82, 0x6c,
+       0xfc, 0x42, 0x7c, 0x7c,
+       0x10, 0x40, 0x7e, 0x6c,
+       0x20, 0x4d, 0xa0, 0x7c,
+       0x08, 0x5d, 0xba, 0x6c,
+       0x00, 0xe2, 0xa0, 0x44,
+       0x02, 0x19, 0x32, 0x00,
+       0x08, 0xa8, 0xa3, 0x7c,
+       0x04, 0x5d, 0x0e, 0x7d,
+       0x01, 0x1a, 0x0e, 0x7d,
+       0x01, 0xa4, 0x49, 0x03,
+       0x80, 0xf9, 0xf2, 0x01,
+       0x02, 0xa8, 0x82, 0x32,
+       0x02, 0xea, 0xb4, 0x00,
+       0x00, 0xe2, 0xba, 0x42,
+       0x02, 0xa8, 0x82, 0x32,
+       0x02, 0xea, 0xb4, 0x00,
+       0xff, 0xea, 0xd4, 0x19,
+       0x00, 0xe2, 0x48, 0x5b,
+       0x00, 0xe2, 0xe8, 0x5c,
+       0x00, 0xe2, 0xba, 0x52,
+       0xff, 0xea, 0xd4, 0x0d,
+       0x00, 0xe2, 0xd4, 0x58,
+       0x40, 0x5b, 0xae, 0x6c,
+       0x04, 0x5d, 0x0e, 0x7d,
+       0x01, 0x1a, 0x0e, 0x7d,
+       0x20, 0x4d, 0xa0, 0x7c,
+       0x40, 0x5b, 0xfc, 0x7c,
+       0x04, 0x5d, 0x0e, 0x7d,
+       0x01, 0x1a, 0x0e, 0x7d,
+       0x80, 0xf9, 0xf2, 0x01,
+       0x01, 0xa4, 0x49, 0x03,
+       0x08, 0xa8, 0x95, 0x6c,
+       0x02, 0xea, 0xb4, 0x04,
+       0x02, 0x19, 0x32, 0x00,
+       0x01, 0x5b, 0x40, 0x31,
+       0x20, 0xea, 0xb6, 0x00,
+       0x00, 0xe2, 0x06, 0x5c,
+       0x00, 0xe2, 0x14, 0x5b,
+       0x00, 0xe2, 0x42, 0x5b,
+       0x20, 0x5c, 0xb8, 0x00,
+       0x80, 0xf9, 0xf2, 0x01,
+       0x20, 0xa0, 0xfc, 0x7c,
+       0x08, 0xa8, 0xd1, 0x7c,
+       0x00, 0xe2, 0x94, 0x44,
+       0x02, 0xea, 0xb4, 0x04,
+       0xff, 0x6a, 0xde, 0x7c,
+       0x14, 0xea, 0x04, 0x00,
+       0x00, 0xe2, 0xde, 0x44,
+       0x00, 0xe2, 0xd2, 0x58,
+       0x10, 0x5d, 0xd2, 0x6c,
+       0x40, 0x5b, 0xa0, 0x7c,
+       0x02, 0x19, 0x32, 0x00,
+       0x80, 0xf9, 0xf2, 0x01,
+       0xff, 0xea, 0x10, 0x03,
+       0x08, 0xa8, 0x51, 0x03,
+       0x00, 0xe2, 0x94, 0x44,
+       0x80, 0xf9, 0xa0, 0x6c,
+       0x01, 0x42, 0xc1, 0x31,
+       0x00, 0xfb, 0xa0, 0x64,
+       0x01, 0x41, 0xc1, 0x31,
+       0x00, 0xfa, 0xa0, 0x64,
+       0x01, 0xe8, 0xd4, 0x1d,
+       0x30, 0x3f, 0xc0, 0x09,
+       0x30, 0xe0, 0xa0, 0x64,
+       0x40, 0x4b, 0xa0, 0x6c,
+       0xff, 0xea, 0x52, 0x01,
+       0xee, 0x00, 0x02, 0x6d,
+       0x80, 0xf9, 0xf2, 0x01,
+       0x02, 0xea, 0xb4, 0x00,
+       0x20, 0xea, 0x9a, 0x00,
+       0xf3, 0x42, 0x0a, 0x6d,
+       0x18, 0xea, 0x04, 0x00,
+       0x00, 0xe2, 0x70, 0x41,
+       0x11, 0xea, 0x04, 0x00,
+       0x00, 0xe2, 0x70, 0x41,
+       0x15, 0xea, 0x04, 0x00,
+       0x00, 0xe2, 0xaa, 0x5a,
+       0x33, 0xea, 0xda, 0x58,
+       0x80, 0x3d, 0x7a, 0x00,
+       0x00, 0xe2, 0x2e, 0x5d,
+       0xa8, 0xea, 0x32, 0x00,
+       0x00, 0xe2, 0xd4, 0x58,
+       0x40, 0x1a, 0x22, 0x7d,
+       0x00, 0xe2, 0x2e, 0x5d,
+       0x80, 0x19, 0x32, 0x00,
+       0x40, 0x5b, 0x26, 0x6d,
+       0x20, 0x4d, 0xa0, 0x7c,
+       0x02, 0x84, 0x09, 0x03,
+       0x40, 0x5b, 0xfc, 0x7c,
+       0x08, 0xa8, 0x8d, 0x6c,
+       0x02, 0xea, 0xb4, 0x04,
+       0x01, 0x37, 0xe1, 0x30,
+       0x05, 0x38, 0xe3, 0x98,
+       0x01, 0xe0, 0xf8, 0x31,
+       0xff, 0xea, 0xc0, 0x09,
+       0x00, 0x39, 0xe5, 0x20,
+       0x00, 0x3a, 0xe7, 0x20,
+       0x01, 0xfc, 0xc0, 0x31,
+       0x04, 0xea, 0xe8, 0x30,
+       0xff, 0xea, 0xf0, 0x08,
+       0x02, 0xea, 0xf2, 0x00,
+       0xff, 0xea, 0xf4, 0x08
+};
+
+static int aic_patch10_func(struct ahd_softc *ahd);
+
+static int
+aic_patch10_func(struct ahd_softc *ahd)
+{
+       return ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) != 0);
+}
+
+static int aic_patch9_func(struct ahd_softc *ahd);
+
+static int
+aic_patch9_func(struct ahd_softc *ahd)
+{
+       return ((ahd->bugs & AHD_PKT_BITBUCKET_BUG) == 0);
+}
+
+static int aic_patch8_func(struct ahd_softc *ahd);
+
+static int
+aic_patch8_func(struct ahd_softc *ahd)
+{
+       return ((ahd->flags & AHD_INITIATORROLE) != 0);
+}
+
+static int aic_patch7_func(struct ahd_softc *ahd);
+
+static int
+aic_patch7_func(struct ahd_softc *ahd)
+{
+       return ((ahd->flags & AHD_TARGETROLE) != 0);
+}
+
+static int aic_patch6_func(struct ahd_softc *ahd);
+
+static int
+aic_patch6_func(struct ahd_softc *ahd)
+{
+       return ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0);
+}
+
+static int aic_patch5_func(struct ahd_softc *ahd);
+
+static int
+aic_patch5_func(struct ahd_softc *ahd)
+{
+       return ((ahd->flags & AHD_39BIT_ADDRESSING) != 0);
+}
+
+static int aic_patch4_func(struct ahd_softc *ahd);
+
+static int
+aic_patch4_func(struct ahd_softc *ahd)
+{
+       return ((ahd->flags & AHD_64BIT_ADDRESSING) != 0);
+}
+
+static int aic_patch3_func(struct ahd_softc *ahd);
+
+static int
+aic_patch3_func(struct ahd_softc *ahd)
+{
+       return ((ahd->flags & AHD_SEQUENCER_DEBUG) != 0);
+}
+
+static int aic_patch2_func(struct ahd_softc *ahd);
+
+static int
+aic_patch2_func(struct ahd_softc *ahd)
+{
+       return ((ahd->bugs & AHD_LQO_ATNO_BUG) != 0);
+}
+
+static int aic_patch1_func(struct ahd_softc *ahd);
+
+static int
+aic_patch1_func(struct ahd_softc *ahd)
+{
+       return ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0);
+}
+
+static int aic_patch0_func(struct ahd_softc *ahd);
+
+static int
+aic_patch0_func(struct ahd_softc *ahd)
+{
+       return (0);
+}
+
+typedef int patch_func_t (struct ahd_softc *ahd);
+static struct patch {
+       patch_func_t    *patch_func;
+       uint32_t        begin      :10,
+                       skip_instr :10,
+                       skip_patch :12;
+} patches[] = {
+       { aic_patch1_func, 112, 1, 1 },
+       { aic_patch1_func, 139, 16, 2 },
+       { aic_patch0_func, 155, 1, 1 },
+       { aic_patch2_func, 175, 2, 1 },
+       { aic_patch3_func, 283, 1, 1 },
+       { aic_patch3_func, 286, 1, 1 },
+       { aic_patch3_func, 288, 1, 1 },
+       { aic_patch3_func, 320, 1, 1 },
+       { aic_patch4_func, 445, 1, 2 },
+       { aic_patch0_func, 446, 1, 1 },
+       { aic_patch5_func, 451, 1, 1 },
+       { aic_patch4_func, 452, 1, 1 },
+       { aic_patch6_func, 504, 1, 1 },
+       { aic_patch7_func, 509, 1, 1 },
+       { aic_patch8_func, 510, 2, 1 },
+       { aic_patch7_func, 513, 1, 2 },
+       { aic_patch0_func, 514, 1, 1 },
+       { aic_patch6_func, 577, 1, 1 },
+       { aic_patch6_func, 605, 1, 1 },
+       { aic_patch6_func, 623, 1, 1 },
+       { aic_patch9_func, 649, 2, 2 },
+       { aic_patch0_func, 651, 2, 1 },
+       { aic_patch10_func, 654, 3, 1 },
+       { aic_patch10_func, 663, 11, 1 }
+};
+static struct cs {
+       u_int16_t       begin;
+       u_int16_t       end;
+} critical_sections[] = {
+       { 4, 5 },
+       { 6, 7 },
+       { 13, 19 },
+       { 20, 30 },
+       { 75, 102 },
+       { 105, 109 },
+       { 112, 120 },
+       { 139, 172 },
+       { 577, 594 },
+       { 599, 605 },
+       { 605, 617 },
+       { 623, 628 },
+       { 628, 634 }
+};
+static const int num_critical_sections = sizeof(critical_sections)
+                                      / sizeof(*critical_sections);
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx.h b/xen/drivers/scsi/aic7xxx/aic7xxx.h
new file mode 100644 (file)
index 0000000..289e0bf
--- /dev/null
@@ -0,0 +1,1245 @@
+/*
+ * Core definitions and data structures shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.h#45 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.h,v 1.16.2.13 2002/04/29 19:36:29 gibbs Exp $
+ */
+
+#ifndef _AIC7XXX_H_
+#define _AIC7XXX_H_
+
+/* Register Definitions */
+#include "aic7xxx_reg.h"
+
+/************************* Forward Declarations *******************************/
+struct ahc_platform_data;
+struct scb_platform_data;
+struct seeprom_descriptor;
+
+/****************************** Useful Macros *********************************/
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define NUM_ELEMENTS(array) (sizeof(array) / sizeof(*array))
+
+#define ALL_CHANNELS '\0'
+#define ALL_TARGETS_MASK 0xFFFF
+#define INITIATOR_WILDCARD     (~0)
+
+#define SCSIID_TARGET(ahc, scsiid) \
+       (((scsiid) & ((((ahc)->features & AHC_TWIN) != 0) ? TWIN_TID : TID)) \
+       >> TID_SHIFT)
+#define SCSIID_OUR_ID(scsiid) \
+       ((scsiid) & OID)
+#define SCSIID_CHANNEL(ahc, scsiid) \
+       ((((ahc)->features & AHC_TWIN) != 0) \
+        ? ((((scsiid) & TWIN_CHNLB) != 0) ? 'B' : 'A') \
+       : 'A')
+#define        SCB_IS_SCSIBUS_B(ahc, scb) \
+       (SCSIID_CHANNEL(ahc, (scb)->hscb->scsiid) == 'B')
+#define        SCB_GET_OUR_ID(scb) \
+       SCSIID_OUR_ID((scb)->hscb->scsiid)
+#define        SCB_GET_TARGET(ahc, scb) \
+       SCSIID_TARGET((ahc), (scb)->hscb->scsiid)
+#define        SCB_GET_CHANNEL(ahc, scb) \
+       SCSIID_CHANNEL(ahc, (scb)->hscb->scsiid)
+#define        SCB_GET_LUN(scb) \
+       ((scb)->hscb->lun)
+#define SCB_GET_TARGET_OFFSET(ahc, scb)        \
+       (SCB_GET_TARGET(ahc, scb) + (SCB_IS_SCSIBUS_B(ahc, scb) ? 8 : 0))
+#define SCB_GET_TARGET_MASK(ahc, scb) \
+       (0x01 << (SCB_GET_TARGET_OFFSET(ahc, scb)))
+#define TCL_TARGET_OFFSET(tcl) \
+       ((((tcl) >> 4) & TID) >> 4)
+#define TCL_LUN(tcl) \
+       (tcl & (AHC_NUM_LUNS - 1))
+#define BUILD_TCL(scsiid, lun) \
+       ((lun) | (((scsiid) & TID) << 4))
+
+#ifndef        AHC_TARGET_MODE
+#undef AHC_TMODE_ENABLE
+#define        AHC_TMODE_ENABLE 0
+#endif
+
+/**************************** Driver Constants ********************************/
+/*
+ * The maximum number of supported targets.
+ */
+#define AHC_NUM_TARGETS 16
+
+/*
+ * The maximum number of supported luns.
+ * The identify message only supports 64 luns in SPI3.
+ * You can have 2^64 luns when information unit transfers are enabled,
+ * but it is doubtful this driver will ever support IUTs.
+ */
+#define AHC_NUM_LUNS 64
+
+/*
+ * The maximum transfer per S/G segment.
+ */
+#define AHC_MAXTRANSFER_SIZE    0x00ffffff     /* limited by 24bit counter */
+
+/*
+ * The maximum amount of SCB storage in hardware on a controller.
+ * This value represents an upper bound.  Controllers vary in the number
+ * they actually support.
+ */
+#define AHC_SCB_MAX    255
+
+/*
+ * The maximum number of concurrent transactions supported per driver instance.
+ * Sequencer Control Blocks (SCBs) store per-transaction information.  Although
+ * the space for SCBs on the host adapter varies by model, the driver will
+ * page the SCBs between host and controller memory as needed.  We are limited
+ * to 253 because:
+ *     1) The 8bit nature of the RISC engine holds us to an 8bit value.
+ *     2) We reserve one value, 255, to represent the invalid element.
+ *     3) Our input queue scheme requires one SCB to always be reserved
+ *        in advance of queuing any SCBs.  This takes us down to 254.
+ *     4) To handle our output queue correctly on machines that only
+ *        support 32bit stores, we must clear the array 4 bytes at a
+ *        time.  To avoid colliding with a DMA write from the sequencer,
+ *        we must be sure that 4 slots are empty when we write to clear
+ *        the queue.  This reduces us to 253 SCBs: 1 that just completed
+ *        and the known three additional empty slots in the queue that
+ *        precede it.
+ */
+#define AHC_MAX_QUEUE  253
+
+/*
+ * The maximum amount of SCB storage we allocate in host memory.  This
+ * number should reflect the 1 additional SCB we require to handle our
+ * qinfifo mechanism.
+ */
+#define AHC_SCB_MAX_ALLOC (AHC_MAX_QUEUE+1)
+
+/*
+ * Ring Buffer of incoming target commands.
+ * We allocate 256 to simplify the logic in the sequencer
+ * by using the natural wrap point of an 8bit counter.
+ */
+#define AHC_TMODE_CMDS 256
+
+/* Reset line assertion time in us */
+#define AHC_BUSRESET_DELAY     250
+
+/******************* Chip Characteristics/Operating Settings  *****************/
+/*
+ * Chip Type
+ * The chip order is from least sophisticated to most sophisticated.
+ */
+typedef enum {
+       AHC_NONE        = 0x0000,
+       AHC_CHIPID_MASK = 0x00FF,
+       AHC_AIC7770     = 0x0001,
+       AHC_AIC7850     = 0x0002,
+       AHC_AIC7855     = 0x0003,
+       AHC_AIC7859     = 0x0004,
+       AHC_AIC7860     = 0x0005,
+       AHC_AIC7870     = 0x0006,
+       AHC_AIC7880     = 0x0007,
+       AHC_AIC7895     = 0x0008,
+       AHC_AIC7895C    = 0x0009,
+       AHC_AIC7890     = 0x000a,
+       AHC_AIC7896     = 0x000b,
+       AHC_AIC7892     = 0x000c,
+       AHC_AIC7899     = 0x000d,
+       AHC_VL          = 0x0100,       /* Bus type VL */
+       AHC_EISA        = 0x0200,       /* Bus type EISA */
+       AHC_PCI         = 0x0400,       /* Bus type PCI */
+       AHC_BUS_MASK    = 0x0F00
+} ahc_chip;
+
+/*
+ * Features available in each chip type.
+ */
+typedef enum {
+       AHC_FENONE      = 0x00000,
+       AHC_ULTRA       = 0x00001,      /* Supports 20MHz Transfers */
+       AHC_ULTRA2      = 0x00002,      /* Supports 40MHz Transfers */
+       AHC_WIDE        = 0x00004,      /* Wide Channel */
+       AHC_TWIN        = 0x00008,      /* Twin Channel */
+       AHC_MORE_SRAM   = 0x00010,      /* 80 bytes instead of 64 */
+       AHC_CMD_CHAN    = 0x00020,      /* Has a Command DMA Channel */
+       AHC_QUEUE_REGS  = 0x00040,      /* Has Queue management registers */
+       AHC_SG_PRELOAD  = 0x00080,      /* Can perform auto-SG preload */
+       AHC_SPIOCAP     = 0x00100,      /* Has a Serial Port I/O Cap Register */
+       AHC_MULTI_TID   = 0x00200,      /* Has bitmask of TIDs for select-in */
+       AHC_HS_MAILBOX  = 0x00400,      /* Has HS_MAILBOX register */
+       AHC_DT          = 0x00800,      /* Double Transition transfers */
+       AHC_NEW_TERMCTL = 0x01000,      /* Newer termination scheme */
+       AHC_MULTI_FUNC  = 0x02000,      /* Multi-Function Twin Channel Device */
+       AHC_LARGE_SCBS  = 0x04000,      /* 64byte SCBs */
+       AHC_AUTORATE    = 0x08000,      /* Automatic update of SCSIRATE/OFFSET*/
+       AHC_AUTOPAUSE   = 0x10000,      /* Automatic pause on register access */
+       AHC_TARGETMODE  = 0x20000,      /* Has tested target mode support */
+       AHC_MULTIROLE   = 0x40000,      /* Space for two roles at a time */
+       AHC_REMOVABLE   = 0x80000,      /* Hot-Swap supported */
+       AHC_AIC7770_FE  = AHC_FENONE,
+       /*
+        * The real 7850 does not support Ultra modes, but there are
+        * several cards that use the generic 7850 PCI ID even though
+        * they are using an Ultra capable chip (7859/7860).  We start
+        * out with the AHC_ULTRA feature set and then check the DEVSTATUS
+        * register to determine if the capability is really present.
+        */
+       AHC_AIC7850_FE  = AHC_SPIOCAP|AHC_AUTOPAUSE|AHC_TARGETMODE|AHC_ULTRA,
+       AHC_AIC7860_FE  = AHC_AIC7850_FE,
+       AHC_AIC7870_FE  = AHC_TARGETMODE,
+       AHC_AIC7880_FE  = AHC_AIC7870_FE|AHC_ULTRA,
+       /*
+        * Although we have space for both the initiator and
+        * target roles on ULTRA2 chips, we currently disable
+        * the initiator role to allow multi-scsi-id target mode
+        * configurations.  We can only respond on the same SCSI
+        * ID as our initiator role if we allow initiator operation.
+        * At some point, we should add a configuration knob to
+        * allow both roles to be loaded.
+        */
+       AHC_AIC7890_FE  = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2
+                         |AHC_QUEUE_REGS|AHC_SG_PRELOAD|AHC_MULTI_TID
+                         |AHC_HS_MAILBOX|AHC_NEW_TERMCTL|AHC_LARGE_SCBS
+                         |AHC_TARGETMODE,
+       AHC_AIC7892_FE  = AHC_AIC7890_FE|AHC_DT|AHC_AUTORATE|AHC_AUTOPAUSE,
+       AHC_AIC7895_FE  = AHC_AIC7880_FE|AHC_MORE_SRAM|AHC_AUTOPAUSE
+                         |AHC_CMD_CHAN|AHC_MULTI_FUNC|AHC_LARGE_SCBS,
+       AHC_AIC7895C_FE = AHC_AIC7895_FE|AHC_MULTI_TID,
+       AHC_AIC7896_FE  = AHC_AIC7890_FE|AHC_MULTI_FUNC,
+       AHC_AIC7899_FE  = AHC_AIC7892_FE|AHC_MULTI_FUNC
+} ahc_feature;
+
+/*
+ * Bugs in the silicon that we work around in software.
+ */
+typedef enum {
+       AHC_BUGNONE             = 0x00,
+       /*
+        * On all chips prior to the U2 product line,
+        * the WIDEODD S/G segment feature does not
+        * work during scsi->HostBus transfers.
+        */
+       AHC_TMODE_WIDEODD_BUG   = 0x01,
+       /*
+        * On the aic7890/91 Rev 0 chips, the autoflush
+        * feature does not work.  A manual flush of
+        * the DMA FIFO is required.
+        */
+       AHC_AUTOFLUSH_BUG       = 0x02,
+       /*
+        * On many chips, cacheline streaming does not work.
+        */
+       AHC_CACHETHEN_BUG       = 0x04,
+       /*
+        * On the aic7896/97 chips, cacheline
+        * streaming must be enabled.
+        */
+       AHC_CACHETHEN_DIS_BUG   = 0x08,
+       /*
+        * PCI 2.1 Retry failure on non-empty data fifo.
+        */
+       AHC_PCI_2_1_RETRY_BUG   = 0x10,
+       /*
+        * Controller does not handle cacheline residuals
+        * properly on S/G segments if PCI MWI instructions
+        * are allowed.
+        */
+       AHC_PCI_MWI_BUG         = 0x20,
+       /*
+        * An SCB upload using the SCB channel's
+        * auto array entry copy feature may 
+        * corrupt data.  This appears to only
+        * occur on 66MHz systems.
+        */
+       AHC_SCBCHAN_UPLOAD_BUG  = 0x40
+} ahc_bug;
+
+/*
+ * Configuration specific settings.
+ * The driver determines these settings by probing the
+ * chip/controller's configuration.
+ */
+typedef enum {
+       AHC_FNONE             = 0x000,
+       AHC_PRIMARY_CHANNEL   = 0x003,/*
+                                        * The channel that should
+                                        * be probed first.
+                                        */
+       AHC_USEDEFAULTS       = 0x004,/*
+                                        * For cards without an seeprom
+                                        * or a BIOS to initialize the chip's
+                                        * SRAM, we use the default target
+                                        * settings.
+                                        */
+       AHC_SEQUENCER_DEBUG   = 0x008,
+       AHC_SHARED_SRAM       = 0x010,
+       AHC_LARGE_SEEPROM     = 0x020,/* Uses C56_66 not C46 */
+       AHC_RESET_BUS_A       = 0x040,
+       AHC_RESET_BUS_B       = 0x080,
+       AHC_EXTENDED_TRANS_A  = 0x100,
+       AHC_EXTENDED_TRANS_B  = 0x200,
+       AHC_TERM_ENB_A        = 0x400,
+       AHC_TERM_ENB_B        = 0x800,
+       AHC_INITIATORROLE     = 0x1000,/*
+                                         * Allow initiator operations on
+                                         * this controller.
+                                         */
+       AHC_TARGETROLE        = 0x2000,/*
+                                         * Allow target operations on this
+                                         * controller.
+                                         */
+       AHC_NEWEEPROM_FMT     = 0x4000,
+       AHC_RESOURCE_SHORTAGE = 0x8000,
+       AHC_TQINFIFO_BLOCKED  = 0x10000,/* Blocked waiting for ATIOs */
+       AHC_INT50_SPEEDFLEX   = 0x20000,/*
+                                          * Internal 50pin connector
+                                          * sits behind an aic3860
+                                          */
+       AHC_SCB_BTT           = 0x40000,/*
+                                          * The busy targets table is
+                                          * stored in SCB space rather
+                                          * than SRAM.
+                                          */
+       AHC_BIOS_ENABLED      = 0x80000,
+       AHC_ALL_INTERRUPTS    = 0x100000,
+       AHC_PAGESCBS          = 0x400000,  /* Enable SCB paging */
+       AHC_EDGE_INTERRUPT    = 0x800000,  /* Device uses edge triggered ints */
+       AHC_39BIT_ADDRESSING  = 0x1000000, /* Use 39 bit addressing scheme. */
+       AHC_LSCBS_ENABLED     = 0x2000000, /* 64Byte SCBs enabled */
+       AHC_SCB_CONFIG_USED   = 0x4000000  /* No SEEPROM but SCB2 had info. */
+} ahc_flag;
+
+/************************* Hardware  SCB Definition ***************************/
+
+/*
+ * The driver keeps up to MAX_SCB scb structures per card in memory.  The SCB
+ * consists of a "hardware SCB" mirroring the fields availible on the card
+ * and additional information the kernel stores for each transaction.
+ *
+ * To minimize space utilization, a portion of the hardware scb stores
+ * different data during different portions of a SCSI transaction.
+ * As initialized by the host driver for the initiator role, this area
+ * contains the SCSI cdb (or a pointer to the  cdb) to be executed.  After
+ * the cdb has been presented to the target, this area serves to store
+ * residual transfer information and the SCSI status byte.
+ * For the target role, the contents of this area do not change, but
+ * still serve a different purpose than for the initiator role.  See
+ * struct target_data for details.
+ */
+
+/*
+ * Status information embedded in the shared poriton of
+ * an SCB after passing the cdb to the target.  The kernel
+ * driver will only read this data for transactions that
+ * complete abnormally (non-zero status byte).
+ */
+struct status_pkt {
+       uint32_t residual_datacnt;      /* Residual in the current S/G seg */
+       uint32_t residual_sg_ptr;       /* The next S/G for this transfer */
+       uint8_t  scsi_status;           /* Standard SCSI status byte */
+};
+
+/*
+ * Target mode version of the shared data SCB segment.
+ */
+struct target_data {
+       uint32_t residual_datacnt;      /* Residual in the current S/G seg */
+       uint32_t residual_sg_ptr;       /* The next S/G for this transfer */
+       uint8_t  scsi_status;           /* SCSI status to give to initiator */
+       uint8_t  target_phases;         /* Bitmap of phases to execute */
+       uint8_t  data_phase;            /* Data-In or Data-Out */
+       uint8_t  initiator_tag;         /* Initiator's transaction tag */
+};
+
+struct hardware_scb {
+/*0*/  union {
+               /*
+                * If the cdb is 12 bytes or less, we embed it directly
+                * in the SCB.  For longer cdbs, we embed the address
+                * of the cdb payload as seen by the chip and a DMA
+                * is used to pull it in.
+                */
+               uint8_t  cdb[12];
+               uint32_t cdb_ptr;
+               struct   status_pkt status;
+               struct   target_data tdata;
+       } shared_data;
+/*
+ * A word about residuals.
+ * The scb is presented to the sequencer with the dataptr and datacnt
+ * fields initialized to the contents of the first S/G element to
+ * transfer.  The sgptr field is initialized to the bus address for
+ * the S/G element that follows the first in the in core S/G array
+ * or'ed with the SG_FULL_RESID flag.  Sgptr may point to an invalid
+ * S/G entry for this transfer (single S/G element transfer with the
+ * first elements address and length preloaded in the dataptr/datacnt
+ * fields).  If no transfer is to occur, sgptr is set to SG_LIST_NULL.
+ * The SG_FULL_RESID flag ensures that the residual will be correctly
+ * noted even if no data transfers occur.  Once the data phase is entered,
+ * the residual sgptr and datacnt are loaded from the sgptr and the
+ * datacnt fields.  After each S/G element's dataptr and length are
+ * loaded into the hardware, the residual sgptr is advanced.  After
+ * each S/G element is expired, its datacnt field is checked to see
+ * if the LAST_SEG flag is set.  If so, SG_LIST_NULL is set in the
+ * residual sg ptr and the transfer is considered complete.  If the
+ * sequencer determines that there is a residual in the tranfer, it
+ * will set the SG_RESID_VALID flag in sgptr and dma the scb back into
+ * host memory.  To sumarize:
+ *
+ * Sequencer:
+ *     o A residual has occurred if SG_FULL_RESID is set in sgptr,
+ *       or residual_sgptr does not have SG_LIST_NULL set.
+ *
+ *     o We are transfering the last segment if residual_datacnt has
+ *       the SG_LAST_SEG flag set.
+ *
+ * Host:
+ *     o A residual has occurred if a completed scb has the
+ *       SG_RESID_VALID flag set.
+ *
+ *     o residual_sgptr and sgptr refer to the "next" sg entry
+ *       and so may point beyond the last valid sg entry for the
+ *       transfer.
+ */ 
+/*12*/ uint32_t dataptr;
+/*16*/ uint32_t datacnt;               /*
+                                        * Byte 3 (numbered from 0) of
+                                        * the datacnt is really the
+                                        * 4th byte in that data address.
+                                        */
+/*20*/ uint32_t sgptr;
+#define SG_PTR_MASK    0xFFFFFFF8
+/*24*/ uint8_t  control;       /* See SCB_CONTROL in aic7xxx.reg for details */
+/*25*/ uint8_t  scsiid;        /* what to load in the SCSIID register */
+/*26*/ uint8_t  lun;
+/*27*/ uint8_t  tag;                   /*
+                                        * Index into our kernel SCB array.
+                                        * Also used as the tag for tagged I/O
+                                        */
+/*28*/ uint8_t  cdb_len;
+/*29*/ uint8_t  scsirate;              /* Value for SCSIRATE register */
+/*30*/ uint8_t  scsioffset;            /* Value for SCSIOFFSET register */
+/*31*/ uint8_t  next;                  /*
+                                        * Used for threading SCBs in the
+                                        * "Waiting for Selection" and
+                                        * "Disconnected SCB" lists down
+                                        * in the sequencer.
+                                        */
+/*32*/ uint8_t  cdb32[32];             /*
+                                        * CDB storage for cdbs of size
+                                        * 13->32.  We store them here
+                                        * because hardware scbs are
+                                        * allocated from DMA safe
+                                        * memory so we are guaranteed
+                                        * the controller can access
+                                        * this data.
+                                        */
+};
+
+/************************ Kernel SCB Definitions ******************************/
+/*
+ * Some fields of the SCB are OS dependent.  Here we collect the
+ * definitions for elements that all OS platforms need to include
+ * in there SCB definition.
+ */
+
+/*
+ * Definition of a scatter/gather element as transfered to the controller.
+ * The aic7xxx chips only support a 24bit length.  We use the top byte of
+ * the length to store additional address bits and a flag to indicate
+ * that a given segment terminates the transfer.  This gives us an
+ * addressable range of 512GB on machines with 64bit PCI or with chips
+ * that can support dual address cycles on 32bit PCI busses.
+ */
+struct ahc_dma_seg {
+       uint32_t        addr;
+       uint32_t        len;
+#define        AHC_DMA_LAST_SEG        0x80000000
+#define        AHC_SG_HIGH_ADDR_MASK   0x7F000000
+#define        AHC_SG_LEN_MASK         0x00FFFFFF
+};
+
+struct sg_map_node {
+       bus_dmamap_t             sg_dmamap;
+       bus_addr_t               sg_physaddr;
+       struct ahc_dma_seg*      sg_vaddr;
+       SLIST_ENTRY(sg_map_node) links;
+};
+
+/*
+ * The current state of this SCB.
+ */
+typedef enum {
+       SCB_FREE                = 0x0000,
+       SCB_OTHERTCL_TIMEOUT    = 0x0002,/*
+                                         * Another device was active
+                                         * during the first timeout for
+                                         * this SCB so we gave ourselves
+                                         * an additional timeout period
+                                         * in case it was hogging the
+                                         * bus.
+                                         */
+       SCB_DEVICE_RESET        = 0x0004,
+       SCB_SENSE               = 0x0008,
+       SCB_CDB32_PTR           = 0x0010,
+       SCB_RECOVERY_SCB        = 0x0020,
+       SCB_AUTO_NEGOTIATE      = 0x0040,/* Negotiate to achieve goal. */
+       SCB_NEGOTIATE           = 0x0080,/* Negotiation forced for command. */
+       SCB_ABORT               = 0x1000,
+       SCB_UNTAGGEDQ           = 0x2000,
+       SCB_ACTIVE              = 0x4000,
+       SCB_TARGET_IMMEDIATE    = 0x8000
+} scb_flag;
+
+struct scb {
+       struct  hardware_scb     *hscb;
+       union {
+               SLIST_ENTRY(scb)  sle;
+               TAILQ_ENTRY(scb)  tqe;
+       } links;
+       LIST_ENTRY(scb)           pending_links;
+       ahc_io_ctx_t              io_ctx;
+       struct ahc_softc         *ahc_softc;
+       scb_flag                  flags;
+#ifndef __linux__
+       bus_dmamap_t              dmamap;
+#endif
+       struct scb_platform_data *platform_data;
+       struct sg_map_node       *sg_map;
+       struct ahc_dma_seg       *sg_list;
+       bus_addr_t                sg_list_phys;
+       u_int                     sg_count;/* How full ahc_dma_seg is */
+};
+
+struct scb_data {
+       SLIST_HEAD(, scb) free_scbs;    /*
+                                        * Pool of SCBs ready to be assigned
+                                        * commands to execute.
+                                        */
+       struct  scb *scbindex[256];     /*
+                                        * Mapping from tag to SCB.
+                                        * As tag identifiers are an
+                                        * 8bit value, we provide space
+                                        * for all possible tag values.
+                                        * Any lookups to entries at or
+                                        * above AHC_SCB_MAX_ALLOC will
+                                        * always fail.
+                                        */
+       struct  hardware_scb    *hscbs; /* Array of hardware SCBs */
+       struct  scb *scbarray;          /* Array of kernel SCBs */
+       struct  scsi_sense_data *sense; /* Per SCB sense data */
+
+       /*
+        * "Bus" addresses of our data structures.
+        */
+       bus_dma_tag_t    hscb_dmat;     /* dmat for our hardware SCB array */
+       bus_dmamap_t     hscb_dmamap;
+       bus_addr_t       hscb_busaddr;
+       bus_dma_tag_t    sense_dmat;
+       bus_dmamap_t     sense_dmamap;
+       bus_addr_t       sense_busaddr;
+       bus_dma_tag_t    sg_dmat;       /* dmat for our sg segments */
+       SLIST_HEAD(, sg_map_node) sg_maps;
+       uint8_t numscbs;
+       uint8_t maxhscbs;               /* Number of SCBs on the card */
+       uint8_t init_level;             /*
+                                        * How far we've initialized
+                                        * this structure.
+                                        */
+};
+
+/************************ Target Mode Definitions *****************************/
+
+/*
+ * Connection desciptor for select-in requests in target mode.
+ */
+struct target_cmd {
+       uint8_t scsiid;         /* Our ID and the initiator's ID */
+       uint8_t identify;       /* Identify message */
+       uint8_t bytes[22];      /* 
+                                * Bytes contains any additional message
+                                * bytes terminated by 0xFF.  The remainder
+                                * is the cdb to execute.
+                                */
+       uint8_t cmd_valid;      /*
+                                * When a command is complete, the firmware
+                                * will set cmd_valid to all bits set.
+                                * After the host has seen the command,
+                                * the bits are cleared.  This allows us
+                                * to just peek at host memory to determine
+                                * if more work is complete. cmd_valid is on
+                                * an 8 byte boundary to simplify setting
+                                * it on aic7880 hardware which only has
+                                * limited direct access to the DMA FIFO.
+                                */
+       uint8_t pad[7];
+};
+
+/*
+ * Number of events we can buffer up if we run out
+ * of immediate notify ccbs.
+ */
+#define AHC_TMODE_EVENT_BUFFER_SIZE 8
+struct ahc_tmode_event {
+       uint8_t initiator_id;
+       uint8_t event_type;     /* MSG type or EVENT_TYPE_BUS_RESET */
+#define        EVENT_TYPE_BUS_RESET 0xFF
+       uint8_t event_arg;
+};
+
+/*
+ * Per enabled lun target mode state.
+ * As this state is directly influenced by the host OS'es target mode
+ * environment, we let the OS module define it.  Forward declare the
+ * structure here so we can store arrays of them, etc. in OS neutral
+ * data structures.
+ */
+#ifdef AHC_TARGET_MODE 
+struct ahc_tmode_lstate {
+       struct cam_path *path;
+       struct ccb_hdr_slist accept_tios;
+       struct ccb_hdr_slist immed_notifies;
+       struct ahc_tmode_event event_buffer[AHC_TMODE_EVENT_BUFFER_SIZE];
+       uint8_t event_r_idx;
+       uint8_t event_w_idx;
+};
+#else
+struct ahc_tmode_lstate;
+#endif
+
+/******************** Transfer Negotiation Datastructures *********************/
+#define AHC_TRANS_CUR          0x01    /* Modify current neogtiation status */
+#define AHC_TRANS_ACTIVE       0x03    /* Assume this target is on the bus */
+#define AHC_TRANS_GOAL         0x04    /* Modify negotiation goal */
+#define AHC_TRANS_USER         0x08    /* Modify user negotiation settings */
+
+/*
+ * Transfer Negotiation Information.
+ */
+struct ahc_transinfo {
+       uint8_t protocol_version;       /* SCSI Revision level */
+       uint8_t transport_version;      /* SPI Revision level */
+       uint8_t width;                  /* Bus width */
+       uint8_t period;                 /* Sync rate factor */
+       uint8_t offset;                 /* Sync offset */
+       uint8_t ppr_options;            /* Parallel Protocol Request options */
+};
+
+/*
+ * Per-initiator current, goal and user transfer negotiation information. */
+struct ahc_initiator_tinfo {
+       uint8_t scsirate;               /* Computed value for SCSIRATE reg */
+       struct ahc_transinfo curr;
+       struct ahc_transinfo goal;
+       struct ahc_transinfo user;
+};
+
+/*
+ * Per enabled target ID state.
+ * Pointers to lun target state as well as sync/wide negotiation information
+ * for each initiator<->target mapping.  For the initiator role we pretend
+ * that we are the target and the targets are the initiators since the
+ * negotiation is the same regardless of role.
+ */
+struct ahc_tmode_tstate {
+       struct ahc_tmode_lstate*        enabled_luns[AHC_NUM_LUNS];
+       struct ahc_initiator_tinfo      transinfo[AHC_NUM_TARGETS];
+
+       /*
+        * Per initiator state bitmasks.
+        */
+       uint16_t         auto_negotiate;/* Auto Negotiation Required */
+       uint16_t         ultraenb;      /* Using ultra sync rate  */
+       uint16_t         discenable;    /* Disconnection allowed  */
+       uint16_t         tagenable;     /* Tagged Queuing allowed */
+};
+
+/*
+ * Data structure for our table of allowed synchronous transfer rates.
+ */
+struct ahc_syncrate {
+       u_int sxfr_u2;  /* Value of the SXFR parameter for Ultra2+ Chips */
+       u_int sxfr;     /* Value of the SXFR parameter for <= Ultra Chips */
+#define                ULTRA_SXFR 0x100        /* Rate Requires Ultra Mode set */
+#define                ST_SXFR    0x010        /* Rate Single Transition Only */
+#define                DT_SXFR    0x040        /* Rate Double Transition Only */
+       uint8_t period; /* Period to send to SCSI target */
+       char *rate;
+};
+
+/*
+ * The synchronouse transfer rate table.
+ */
+extern struct ahc_syncrate ahc_syncrates[];
+
+/*
+ * Indexes into our table of syncronous transfer rates.
+ */
+#define AHC_SYNCRATE_DT                0
+#define AHC_SYNCRATE_ULTRA2    1
+#define AHC_SYNCRATE_ULTRA     3
+#define AHC_SYNCRATE_FAST      6
+
+/***************************** Lookup Tables **********************************/
+/*
+ * Phase -> name and message out response
+ * to parity errors in each phase table. 
+ */
+struct ahc_phase_table_entry {
+        uint8_t phase;
+        uint8_t mesg_out; /* Message response to parity errors */
+       char *phasemsg;
+};
+
+/************************** Serial EEPROM Format ******************************/
+
+struct seeprom_config {
+/*
+ * Per SCSI ID Configuration Flags
+ */
+       uint16_t device_flags[16];      /* words 0-15 */
+#define                CFXFER          0x0007  /* synchronous transfer rate */
+#define                CFSYNCH         0x0008  /* enable synchronous transfer */
+#define                CFDISC          0x0010  /* enable disconnection */
+#define                CFWIDEB         0x0020  /* wide bus device */
+#define                CFSYNCHISULTRA  0x0040  /* CFSYNCH is an ultra offset (2940AU)*/
+#define                CFSYNCSINGLE    0x0080  /* Single-Transition signalling */
+#define                CFSTART         0x0100  /* send start unit SCSI command */
+#define                CFINCBIOS       0x0200  /* include in BIOS scan */
+#define                CFRNFOUND       0x0400  /* report even if not found */
+#define                CFMULTILUNDEV   0x0800  /* Probe multiple luns in BIOS scan */
+#define                CFWBCACHEENB    0x4000  /* Enable W-Behind Cache on disks */
+#define                CFWBCACHENOP    0xc000  /* Don't touch W-Behind Cache */
+
+/*
+ * BIOS Control Bits
+ */
+       uint16_t bios_control;          /* word 16 */
+#define                CFSUPREM        0x0001  /* support all removeable drives */
+#define                CFSUPREMB       0x0002  /* support removeable boot drives */
+#define                CFBIOSEN        0x0004  /* BIOS enabled */
+#define                CFBIOS_BUSSCAN  0x0008  /* Have the BIOS Scan the Bus */
+#define                CFSM2DRV        0x0010  /* support more than two drives */
+#define                CFSTPWLEVEL     0x0010  /* Termination level control */
+#define                CF284XEXTEND    0x0020  /* extended translation (284x cards) */ 
+#define                CFCTRL_A        0x0020  /* BIOS displays Ctrl-A message */      
+#define                CFTERM_MENU     0x0040  /* BIOS displays termination menu */    
+#define                CFEXTEND        0x0080  /* extended translation enabled */
+#define                CFSCAMEN        0x0100  /* SCAM enable */
+#define                CFMSG_LEVEL     0x0600  /* BIOS Message Level */
+#define                        CFMSG_VERBOSE   0x0000
+#define                        CFMSG_SILENT    0x0200
+#define                        CFMSG_DIAG      0x0400
+#define                CFBOOTCD        0x0800  /* Support Bootable CD-ROM */
+/*             UNUSED          0xff00  */
+
+/*
+ * Host Adapter Control Bits
+ */
+       uint16_t adapter_control;       /* word 17 */   
+#define                CFAUTOTERM      0x0001  /* Perform Auto termination */
+#define                CFULTRAEN       0x0002  /* Ultra SCSI speed enable */
+#define                CF284XSELTO     0x0003  /* Selection timeout (284x cards) */
+#define                CF284XFIFO      0x000C  /* FIFO Threshold (284x cards) */
+#define                CFSTERM         0x0004  /* SCSI low byte termination */
+#define                CFWSTERM        0x0008  /* SCSI high byte termination */
+#define                CFSPARITY       0x0010  /* SCSI parity */
+#define                CF284XSTERM     0x0020  /* SCSI low byte term (284x cards) */   
+#define                CFMULTILUN      0x0020
+#define                CFRESETB        0x0040  /* reset SCSI bus at boot */
+#define                CFCLUSTERENB    0x0080  /* Cluster Enable */
+#define                CFBOOTCHAN      0x0300  /* probe this channel first */
+#define                CFBOOTCHANSHIFT 8
+#define                CFSEAUTOTERM    0x0400  /* Ultra2 Perform secondary Auto Term*/
+#define                CFSELOWTERM     0x0800  /* Ultra2 secondary low term */
+#define                CFSEHIGHTERM    0x1000  /* Ultra2 secondary high term */
+#define                CFDOMAINVAL     0x4000  /* Perform Domain Validation*/
+
+/*
+ * Bus Release Time, Host Adapter ID
+ */
+       uint16_t brtime_id;             /* word 18 */
+#define                CFSCSIID        0x000f  /* host adapter SCSI ID */
+/*             UNUSED          0x00f0  */
+#define                CFBRTIME        0xff00  /* bus release time */
+
+/*
+ * Maximum targets
+ */
+       uint16_t max_targets;           /* word 19 */   
+#define                CFMAXTARG       0x00ff  /* maximum targets */
+#define                CFBOOTLUN       0x0f00  /* Lun to boot from */
+#define                CFBOOTID        0xf000  /* Target to boot from */
+       uint16_t res_1[10];             /* words 20-29 */
+       uint16_t signature;             /* Signature == 0x250 */
+#define                CFSIGNATURE     0x250
+#define                CFSIGNATURE2    0x300
+       uint16_t checksum;              /* word 31 */
+};
+
+/****************************  Message Buffer *********************************/
+typedef enum {
+       MSG_TYPE_NONE                   = 0x00,
+       MSG_TYPE_INITIATOR_MSGOUT       = 0x01,
+       MSG_TYPE_INITIATOR_MSGIN        = 0x02,
+       MSG_TYPE_TARGET_MSGOUT          = 0x03,
+       MSG_TYPE_TARGET_MSGIN           = 0x04
+} ahc_msg_type;
+
+typedef enum {
+       MSGLOOP_IN_PROG,
+       MSGLOOP_MSGCOMPLETE,
+       MSGLOOP_TERMINATED
+} msg_loop_stat;
+
+/*********************** Software Configuration Structure *********************/
+TAILQ_HEAD(scb_tailq, scb);
+
+struct ahc_suspend_channel_state {
+       uint8_t scsiseq;
+       uint8_t sxfrctl0;
+       uint8_t sxfrctl1;
+       uint8_t simode0;
+       uint8_t simode1;
+       uint8_t seltimer;
+       uint8_t seqctl;
+};
+
+struct ahc_suspend_state {
+       struct  ahc_suspend_channel_state channel[2];
+       uint8_t optionmode;
+       uint8_t dscommand0;
+       uint8_t dspcistatus;
+       /* hsmailbox */
+       uint8_t crccontrol1;
+       uint8_t scbbaddr;
+       /* Host and sequencer SCB counts */
+       uint8_t dff_thrsh;
+       uint8_t *scratch_ram;
+       uint8_t *btt;
+};
+
+typedef void (*ahc_bus_intr_t)(struct ahc_softc *);
+
+struct ahc_softc {
+       bus_space_tag_t           tag;
+       bus_space_handle_t        bsh;
+#ifndef __linux__
+       bus_dma_tag_t             buffer_dmat;   /* dmat for buffer I/O */
+#endif
+       struct scb_data          *scb_data;
+
+       struct scb               *next_queued_scb;
+
+       /*
+        * SCBs that have been sent to the controller
+        */
+       LIST_HEAD(, scb)          pending_scbs;
+
+       /*
+        * Counting lock for deferring the release of additional
+        * untagged transactions from the untagged_queues.  When
+        * the lock is decremented to 0, all queues in the
+        * untagged_queues array are run.
+        */
+       u_int                     untagged_queue_lock;
+
+       /*
+        * Per-target queue of untagged-transactions.  The
+        * transaction at the head of the queue is the
+        * currently pending untagged transaction for the
+        * target.  The driver only allows a single untagged
+        * transaction per target.
+        */
+       struct scb_tailq          untagged_queues[AHC_NUM_TARGETS];
+
+       /*
+        * Platform specific data.
+        */
+       struct ahc_platform_data *platform_data;
+
+       /*
+        * Platform specific device information.
+        */
+       ahc_dev_softc_t           dev_softc;
+
+       /*
+        * Bus specific device information.
+        */
+       ahc_bus_intr_t            bus_intr;
+
+       /*
+        * Target mode related state kept on a per enabled lun basis.
+        * Targets that are not enabled will have null entries.
+        * As an initiator, we keep one target entry for our initiator
+        * ID to store our sync/wide transfer settings.
+        */
+       struct ahc_tmode_tstate  *enabled_targets[AHC_NUM_TARGETS];
+
+       /*
+        * The black hole device responsible for handling requests for
+        * disabled luns on enabled targets.
+        */
+       struct ahc_tmode_lstate  *black_hole;
+
+       /*
+        * Device instance currently on the bus awaiting a continue TIO
+        * for a command that was not given the disconnect priveledge.
+        */
+       struct ahc_tmode_lstate  *pending_device;
+
+       /*
+        * Card characteristics
+        */
+       ahc_chip                  chip;
+       ahc_feature               features;
+       ahc_bug                   bugs;
+       ahc_flag                  flags;
+       struct seeprom_config    *seep_config;
+
+       /* Values to store in the SEQCTL register for pause and unpause */
+       uint8_t                   unpause;
+       uint8_t                   pause;
+
+       /* Command Queues */
+       uint8_t                   qoutfifonext;
+       uint8_t                   qinfifonext;
+       uint8_t                  *qoutfifo;
+       uint8_t                  *qinfifo;
+
+       /* Critical Section Data */
+       struct cs                *critical_sections;
+       u_int                     num_critical_sections;
+
+       /* Links for chaining softcs */
+       TAILQ_ENTRY(ahc_softc)    links;
+
+       /* Channel Names ('A', 'B', etc.) */
+       char                      channel;
+       char                      channel_b;
+
+       /* Initiator Bus ID */
+       uint8_t                   our_id;
+       uint8_t                   our_id_b;
+
+       /*
+        * PCI error detection.
+        */
+       int                       unsolicited_ints;
+
+       /*
+        * Target incoming command FIFO.
+        */
+       struct target_cmd        *targetcmds;
+       uint8_t                   tqinfifonext;
+
+       /*
+        * Incoming and outgoing message handling.
+        */
+       uint8_t                   send_msg_perror;
+       ahc_msg_type              msg_type;
+       uint8_t                   msgout_buf[12];/* Message we are sending */
+       uint8_t                   msgin_buf[12];/* Message we are receiving */
+       u_int                     msgout_len;   /* Length of message to send */
+       u_int                     msgout_index; /* Current index in msgout */
+       u_int                     msgin_index;  /* Current index in msgin */
+
+       /*
+        * Mapping information for data structures shared
+        * between the sequencer and kernel.
+        */
+       bus_dma_tag_t             parent_dmat;
+       bus_dma_tag_t             shared_data_dmat;
+       bus_dmamap_t              shared_data_dmamap;
+       bus_addr_t                shared_data_busaddr;
+
+       /*
+        * Bus address of the one byte buffer used to
+        * work-around a DMA bug for chips <= aic7880
+        * in target mode.
+        */
+       bus_addr_t                dma_bug_buf;
+
+       /* Information saved through suspend/resume cycles */
+       struct ahc_suspend_state  suspend_state;
+
+       /* Number of enabled target mode device on this card */
+       u_int                     enabled_luns;
+
+       /* Initialization level of this data structure */
+       u_int                     init_level;
+
+       /* PCI cacheline size. */
+       u_int                     pci_cachesize;
+
+       /* Per-Unit descriptive information */
+       const char               *description;
+       char                     *name;
+       int                       unit;
+
+       /* Selection Timer settings */
+       int                       seltime;
+       int                       seltime_b;
+
+       uint16_t                  user_discenable;/* Disconnection allowed  */
+       uint16_t                  user_tagenable;/* Tagged Queuing allowed */
+};
+
+TAILQ_HEAD(ahc_softc_tailq, ahc_softc);
+extern struct ahc_softc_tailq ahc_tailq;
+
+/************************ Active Device Information ***************************/
+typedef enum {
+       ROLE_UNKNOWN,
+       ROLE_INITIATOR,
+       ROLE_TARGET
+} role_t;
+
+struct ahc_devinfo {
+       int      our_scsiid;
+       int      target_offset;
+       uint16_t target_mask;
+       u_int    target;
+       u_int    lun;
+       char     channel;
+       role_t   role;          /*
+                                * Only guaranteed to be correct if not
+                                * in the busfree state.
+                                */
+};
+
+/****************************** PCI Structures ********************************/
+typedef int (ahc_device_setup_t)(struct ahc_softc *);
+
+struct ahc_pci_identity {
+       uint64_t                 full_id;
+       uint64_t                 id_mask;
+       char                    *name;
+       ahc_device_setup_t      *setup;
+};
+extern struct ahc_pci_identity ahc_pci_ident_table [];
+extern const u_int ahc_num_pci_devs;
+
+/***************************** VL/EISA Declarations ***************************/
+struct aic7770_identity {
+       uint32_t                 full_id;
+       uint32_t                 id_mask;
+       char                    *name;
+       ahc_device_setup_t      *setup;
+};
+extern struct aic7770_identity aic7770_ident_table [];
+extern const int ahc_num_aic7770_devs;
+
+#define AHC_EISA_SLOT_OFFSET   0xc00
+#define AHC_EISA_IOSIZE                0x100
+
+/*************************** Function Declarations ****************************/
+/******************************************************************************/
+u_int                  ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl);
+void                   ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl);
+void                   ahc_busy_tcl(struct ahc_softc *ahc,
+                                    u_int tcl, u_int busyid);
+
+/***************************** PCI Front End *********************************/
+struct ahc_pci_identity        *ahc_find_pci_device(ahc_dev_softc_t);
+int                     ahc_pci_config(struct ahc_softc *,
+                                       struct ahc_pci_identity *);
+
+/*************************** EISA/VL Front End ********************************/
+struct aic7770_identity *aic7770_find_device(uint32_t);
+int                     aic7770_config(struct ahc_softc *ahc,
+                                       struct aic7770_identity *,
+                                       u_int port);
+
+/************************** SCB and SCB queue management **********************/
+int            ahc_probe_scbs(struct ahc_softc *);
+void           ahc_run_untagged_queues(struct ahc_softc *ahc);
+void           ahc_run_untagged_queue(struct ahc_softc *ahc,
+                                      struct scb_tailq *queue);
+void           ahc_qinfifo_requeue_tail(struct ahc_softc *ahc,
+                                        struct scb *scb);
+int            ahc_match_scb(struct ahc_softc *ahc, struct scb *scb,
+                             int target, char channel, int lun,
+                             u_int tag, role_t role);
+
+/****************************** Initialization ********************************/
+struct ahc_softc       *ahc_alloc(void *platform_arg, char *name);
+int                     ahc_softc_init(struct ahc_softc *);
+void                    ahc_controller_info(struct ahc_softc *ahc, char *buf);
+int                     ahc_init(struct ahc_softc *ahc);
+void                    ahc_intr_enable(struct ahc_softc *ahc, int enable);
+void                    ahc_pause_and_flushwork(struct ahc_softc *ahc);
+int                     ahc_suspend(struct ahc_softc *ahc); 
+int                     ahc_resume(struct ahc_softc *ahc);
+void                    ahc_softc_insert(struct ahc_softc *);
+struct ahc_softc       *ahc_find_softc(struct ahc_softc *ahc);
+void                    ahc_set_unit(struct ahc_softc *, int);
+void                    ahc_set_name(struct ahc_softc *, char *);
+void                    ahc_alloc_scbs(struct ahc_softc *ahc);
+void                    ahc_free(struct ahc_softc *ahc);
+int                     ahc_reset(struct ahc_softc *ahc);
+void                    ahc_shutdown(void *arg);
+
+/*************************** Interrupt Services *******************************/
+void                   ahc_pci_intr(struct ahc_softc *ahc);
+void                   ahc_clear_intstat(struct ahc_softc *ahc);
+void                   ahc_run_qoutfifo(struct ahc_softc *ahc);
+#ifdef AHC_TARGET_MODE
+void                   ahc_run_tqinfifo(struct ahc_softc *ahc, int paused);
+#endif
+void                   ahc_handle_brkadrint(struct ahc_softc *ahc);
+void                   ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat);
+void                   ahc_handle_scsiint(struct ahc_softc *ahc,
+                                          u_int intstat);
+void                   ahc_clear_critical_section(struct ahc_softc *ahc);
+
+/***************************** Error Recovery *********************************/
+typedef enum {
+       SEARCH_COMPLETE,
+       SEARCH_COUNT,
+       SEARCH_REMOVE
+} ahc_search_action;
+int                    ahc_search_qinfifo(struct ahc_softc *ahc, int target,
+                                          char channel, int lun, u_int tag,
+                                          role_t role, uint32_t status,
+                                          ahc_search_action action);
+int                    ahc_search_untagged_queues(struct ahc_softc *ahc,
+                                                  ahc_io_ctx_t ctx,
+                                                  int target, char channel,
+                                                  int lun, uint32_t status,
+                                                  ahc_search_action action);
+int                    ahc_search_disc_list(struct ahc_softc *ahc, int target,
+                                            char channel, int lun, u_int tag,
+                                            int stop_on_first, int remove,
+                                            int save_state);
+void                   ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb);
+int                    ahc_reset_channel(struct ahc_softc *ahc, char channel,
+                                         int initiate_reset);
+int                    ahc_abort_scbs(struct ahc_softc *ahc, int target,
+                                      char channel, int lun, u_int tag,
+                                      role_t role, uint32_t status);
+void                   ahc_restart(struct ahc_softc *ahc);
+void                   ahc_calc_residual(struct ahc_softc *ahc,
+                                         struct scb *scb);
+/*************************** Utility Functions ********************************/
+struct ahc_phase_table_entry*
+                       ahc_lookup_phase_entry(int phase);
+void                   ahc_compile_devinfo(struct ahc_devinfo *devinfo,
+                                           u_int our_id, u_int target,
+                                           u_int lun, char channel,
+                                           role_t role);
+/************************** Transfer Negotiation ******************************/
+struct ahc_syncrate*   ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
+                                         u_int *ppr_options, u_int maxsync);
+u_int                  ahc_find_period(struct ahc_softc *ahc,
+                                       u_int scsirate, u_int maxsync);
+void                   ahc_validate_offset(struct ahc_softc *ahc,
+                                           struct ahc_initiator_tinfo *tinfo,
+                                           struct ahc_syncrate *syncrate,
+                                           u_int *offset, int wide,
+                                           role_t role);
+void                   ahc_validate_width(struct ahc_softc *ahc,
+                                          struct ahc_initiator_tinfo *tinfo,
+                                          u_int *bus_width,
+                                          role_t role);
+int                    ahc_update_neg_request(struct ahc_softc*,
+                                              struct ahc_devinfo*,
+                                              struct ahc_tmode_tstate*,
+                                              struct ahc_initiator_tinfo*,
+                                              int /*force*/);
+void                   ahc_set_width(struct ahc_softc *ahc,
+                                     struct ahc_devinfo *devinfo,
+                                     u_int width, u_int type, int paused);
+void                   ahc_set_syncrate(struct ahc_softc *ahc,
+                                        struct ahc_devinfo *devinfo,
+                                        struct ahc_syncrate *syncrate,
+                                        u_int period, u_int offset,
+                                        u_int ppr_options,
+                                        u_int type, int paused);
+typedef enum {
+       AHC_QUEUE_NONE,
+       AHC_QUEUE_BASIC,
+       AHC_QUEUE_TAGGED
+} ahc_queue_alg;
+
+void                   ahc_set_tags(struct ahc_softc *ahc,
+                                    struct ahc_devinfo *devinfo,
+                                    ahc_queue_alg alg);
+
+/**************************** Target Mode *************************************/
+#ifdef AHC_TARGET_MODE
+void           ahc_send_lstate_events(struct ahc_softc *,
+                                      struct ahc_tmode_lstate *);
+void           ahc_handle_en_lun(struct ahc_softc *ahc,
+                                 struct cam_sim *sim, union ccb *ccb);
+cam_status     ahc_find_tmode_devs(struct ahc_softc *ahc,
+                                   struct cam_sim *sim, union ccb *ccb,
+                                   struct ahc_tmode_tstate **tstate,
+                                   struct ahc_tmode_lstate **lstate,
+                                   int notfound_failure);
+#ifndef AHC_TMODE_ENABLE
+#define AHC_TMODE_ENABLE 0
+#endif
+#endif
+/******************************* Debug ***************************************/
+#ifdef AHC_DEBUG
+extern int ahc_debug;
+#define        AHC_SHOWMISC    0x1
+#define        AHC_SHOWSENSE   0x2
+#endif
+void                   ahc_print_scb(struct scb *scb);
+void                   ahc_dump_card_state(struct ahc_softc *ahc);
+/******************************* SEEPROM *************************************/
+int            ahc_acquire_seeprom(struct ahc_softc *ahc,
+                                   struct seeprom_descriptor *sd);
+void           ahc_release_seeprom(struct seeprom_descriptor *sd);
+#endif /* _AIC7XXX_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx.reg b/xen/drivers/scsi/aic7xxx/aic7xxx.reg
new file mode 100644 (file)
index 0000000..954038b
--- /dev/null
@@ -0,0 +1,1588 @@
+/*
+ * Aic7xxx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.reg,v 1.20.2.11 2002/04/29 19:36:30 gibbs Exp $
+ */
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#30 $"
+
+/*
+ * This file is processed by the aic7xxx_asm utility for use in assembling
+ * firmware for the aic7xxx family of SCSI host adapters as well as to generate
+ * a C header file for use in the kernel portion of the Aic7xxx driver.
+ *
+ * All page numbers refer to the Adaptec AIC-7770 Data Book available from
+ * Adaptec's Technical Documents Department 1-800-934-2766
+ */
+
+/*
+ * SCSI Sequence Control (p. 3-11).
+ * Each bit, when set starts a specific SCSI sequence on the bus
+ */
+register SCSISEQ {
+       address                 0x000
+       access_mode RW
+       bit     TEMODE          0x80
+       bit     ENSELO          0x40
+       bit     ENSELI          0x20
+       bit     ENRSELI         0x10
+       bit     ENAUTOATNO      0x08
+       bit     ENAUTOATNI      0x04
+       bit     ENAUTOATNP      0x02
+       bit     SCSIRSTO        0x01
+}
+
+/*
+ * SCSI Transfer Control 0 Register (pp. 3-13).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL0 {
+       address                 0x001
+       access_mode RW
+       bit     DFON            0x80
+       bit     DFPEXP          0x40
+       bit     FAST20          0x20
+       bit     CLRSTCNT        0x10
+       bit     SPIOEN          0x08
+       bit     SCAMEN          0x04
+       bit     CLRCHN          0x02
+}
+
+/*
+ * SCSI Transfer Control 1 Register (pp. 3-14,15).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL1 {
+       address                 0x002
+       access_mode RW
+       bit     BITBUCKET       0x80
+       bit     SWRAPEN         0x40
+       bit     ENSPCHK         0x20
+       mask    STIMESEL        0x18
+       bit     ENSTIMER        0x04
+       bit     ACTNEGEN        0x02
+       bit     STPWEN          0x01    /* Powered Termination */
+}
+
+/*
+ * SCSI Control Signal Read Register (p. 3-15).
+ * Reads the actual state of the SCSI bus pins
+ */
+register SCSISIGI {
+       address                 0x003
+       access_mode RO
+       bit     CDI             0x80
+       bit     IOI             0x40
+       bit     MSGI            0x20
+       bit     ATNI            0x10
+       bit     SELI            0x08
+       bit     BSYI            0x04
+       bit     REQI            0x02
+       bit     ACKI            0x01
+/*
+ * Possible phases in SCSISIGI
+ */
+       mask    PHASE_MASK      CDI|IOI|MSGI
+       mask    P_DATAOUT       0x00
+       mask    P_DATAIN        IOI
+       mask    P_DATAOUT_DT    P_DATAOUT|MSGI
+       mask    P_DATAIN_DT     P_DATAIN|MSGI
+       mask    P_COMMAND       CDI
+       mask    P_MESGOUT       CDI|MSGI
+       mask    P_STATUS        CDI|IOI
+       mask    P_MESGIN        CDI|IOI|MSGI
+}
+
+/*
+ * SCSI Control Signal Write Register (p. 3-16).
+ * Writing to this register modifies the control signals on the bus.  Only
+ * those signals that are allowed in the current mode (Initiator/Target) are
+ * asserted.
+ */
+register SCSISIGO {
+       address                 0x003
+       access_mode WO
+       bit     CDO             0x80
+       bit     IOO             0x40
+       bit     MSGO            0x20
+       bit     ATNO            0x10
+       bit     SELO            0x08
+       bit     BSYO            0x04
+       bit     REQO            0x02
+       bit     ACKO            0x01
+/*
+ * Possible phases to write into SCSISIG0
+ */
+       mask    PHASE_MASK      CDI|IOI|MSGI
+       mask    P_DATAOUT       0x00
+       mask    P_DATAIN        IOI
+       mask    P_COMMAND       CDI
+       mask    P_MESGOUT       CDI|MSGI
+       mask    P_STATUS        CDI|IOI
+       mask    P_MESGIN        CDI|IOI|MSGI
+}
+
+/* 
+ * SCSI Rate Control (p. 3-17).
+ * Contents of this register determine the Synchronous SCSI data transfer
+ * rate and the maximum synchronous Req/Ack offset.  An offset of 0 in the
+ * SOFS (3:0) bits disables synchronous data transfers.  Any offset value
+ * greater than 0 enables synchronous transfers.
+ */
+register SCSIRATE {
+       address                 0x004
+       access_mode RW
+       bit     WIDEXFER        0x80            /* Wide transfer control */
+       bit     ENABLE_CRC      0x40            /* CRC for D-Phases */
+       bit     SINGLE_EDGE     0x10            /* Disable DT Transfers */
+       mask    SXFR            0x70            /* Sync transfer rate */
+       mask    SXFR_ULTRA2     0x0f            /* Sync transfer rate */
+       mask    SOFS            0x0f            /* Sync offset */
+}
+
+/*
+ * SCSI ID (p. 3-18).
+ * Contains the ID of the board and the current target on the
+ * selected channel.
+ */
+register SCSIID        {
+       address                 0x005
+       access_mode RW
+       mask    TID             0xf0            /* Target ID mask */
+       mask    TWIN_TID        0x70
+       bit     TWIN_CHNLB      0x80
+       mask    OID             0x0f            /* Our ID mask */
+       /*
+        * SCSI Maximum Offset (p. 4-61 aic7890/91 Data Book)
+        * The aic7890/91 allow an offset of up to 127 transfers in both wide
+        * and narrow mode.
+        */
+       alias   SCSIOFFSET
+       mask    SOFS_ULTRA2     0x7f            /* Sync offset U2 chips */
+}
+
+/*
+ * SCSI Latched Data (p. 3-19).
+ * Read/Write latches used to transfer data on the SCSI bus during
+ * Automatic or Manual PIO mode.  SCSIDATH can be used for the
+ * upper byte of a 16bit wide asynchronouse data phase transfer.
+ */
+register SCSIDATL {
+       address                 0x006
+       access_mode RW
+}
+
+register SCSIDATH {
+       address                 0x007
+       access_mode RW
+}
+
+/*
+ * SCSI Transfer Count (pp. 3-19,20)
+ * These registers count down the number of bytes transferred
+ * across the SCSI bus.  The counter is decremented only once
+ * the data has been safely transferred.  SDONE in SSTAT0 is
+ * set when STCNT goes to 0
+ */ 
+register STCNT {
+       address                 0x008
+       size    3
+       access_mode RW
+}
+
+/* ALT_MODE register on Ultra160 chips */
+register OPTIONMODE {
+       address                 0x008
+       access_mode RW
+       bit     AUTORATEEN              0x80
+       bit     AUTOACKEN               0x40
+       bit     ATNMGMNTEN              0x20
+       bit     BUSFREEREV              0x10
+       bit     EXPPHASEDIS             0x08
+       bit     SCSIDATL_IMGEN          0x04
+       bit     AUTO_MSGOUT_DE          0x02
+       bit     DIS_MSGIN_DUALEDGE      0x01
+       mask    OPTIONMODE_DEFAULTS     AUTO_MSGOUT_DE|DIS_MSGIN_DUALEDGE
+}
+
+/* ALT_MODE register on Ultra160 chips */
+register TARGCRCCNT {
+       address                 0x00a
+       size    2
+       access_mode RW
+}
+
+/*
+ * Clear SCSI Interrupt 0 (p. 3-20)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
+ */
+register CLRSINT0 {
+       address                 0x00b
+       access_mode WO
+       bit     CLRSELDO        0x40
+       bit     CLRSELDI        0x20
+       bit     CLRSELINGO      0x10
+       bit     CLRSWRAP        0x08
+       bit     CLRIOERR        0x08    /* Ultra2 Only */
+       bit     CLRSPIORDY      0x02
+}
+
+/*
+ * SCSI Status 0 (p. 3-21)
+ * Contains one set of SCSI Interrupt codes
+ * These are most likely of interest to the sequencer
+ */
+register SSTAT0        {
+       address                 0x00b
+       access_mode RO
+       bit     TARGET          0x80    /* Board acting as target */
+       bit     SELDO           0x40    /* Selection Done */
+       bit     SELDI           0x20    /* Board has been selected */
+       bit     SELINGO         0x10    /* Selection In Progress */
+       bit     SWRAP           0x08    /* 24bit counter wrap */
+       bit     IOERR           0x08    /* LVD Tranceiver mode changed */
+       bit     SDONE           0x04    /* STCNT = 0x000000 */
+       bit     SPIORDY         0x02    /* SCSI PIO Ready */
+       bit     DMADONE         0x01    /* DMA transfer completed */
+}
+
+/*
+ * Clear SCSI Interrupt 1 (p. 3-23)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+register CLRSINT1 {
+       address                 0x00c
+       access_mode WO
+       bit     CLRSELTIMEO     0x80
+       bit     CLRATNO         0x40
+       bit     CLRSCSIRSTI     0x20
+       bit     CLRBUSFREE      0x08
+       bit     CLRSCSIPERR     0x04
+       bit     CLRPHASECHG     0x02
+       bit     CLRREQINIT      0x01
+}
+
+/*
+ * SCSI Status 1 (p. 3-24)
+ */
+register SSTAT1        {
+       address                 0x00c
+       access_mode RO
+       bit     SELTO           0x80
+       bit     ATNTARG         0x40
+       bit     SCSIRSTI        0x20
+       bit     PHASEMIS        0x10
+       bit     BUSFREE         0x08
+       bit     SCSIPERR        0x04
+       bit     PHASECHG        0x02
+       bit     REQINIT         0x01
+}
+
+/*
+ * SCSI Status 2 (pp. 3-25,26)
+ */
+register SSTAT2 {
+       address                 0x00d
+       access_mode RO
+       bit     OVERRUN         0x80
+       bit     SHVALID         0x40    /* Shaddow Layer non-zero */
+       bit     EXP_ACTIVE      0x10    /* SCSI Expander Active */
+       bit     CRCVALERR       0x08    /* CRC doesn't match (U3 only) */
+       bit     CRCENDERR       0x04    /* No terminal CRC packet (U3 only) */
+       bit     CRCREQERR       0x02    /* Illegal CRC packet req (U3 only) */
+       bit     DUAL_EDGE_ERR   0x01    /* Incorrect data phase (U3 only) */
+       mask    SFCNT           0x1f
+}
+
+/*
+ * SCSI Status 3 (p. 3-26)
+ */
+register SSTAT3 {
+       address                 0x00e
+       access_mode RO
+       mask    SCSICNT         0xf0
+       mask    OFFCNT          0x0f
+       mask    U2OFFCNT        0x7f
+}
+
+/*
+ * SCSI ID for the aic7890/91 chips
+ */
+register SCSIID_ULTRA2 {
+       address                 0x00f
+       access_mode RW
+       mask    TID             0xf0            /* Target ID mask */
+       mask    OID             0x0f            /* Our ID mask */
+}
+
+/*
+ * SCSI Interrupt Mode 1 (p. 3-28)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE0 to interrupt via the IRQ pin.
+ */
+register SIMODE0 {
+       address                 0x010
+       access_mode RW
+       bit     ENSELDO         0x40
+       bit     ENSELDI         0x20
+       bit     ENSELINGO       0x10
+       bit     ENSWRAP         0x08
+       bit     ENIOERR         0x08    /* LVD Tranceiver mode changes */
+       bit     ENSDONE         0x04
+       bit     ENSPIORDY       0x02
+       bit     ENDMADONE       0x01
+}
+
+/*
+ * SCSI Interrupt Mode 1 (pp. 3-28,29)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE1 to interrupt via the IRQ pin.
+ */
+register SIMODE1 {
+       address                 0x011
+       access_mode RW
+       bit     ENSELTIMO       0x80
+       bit     ENATNTARG       0x40
+       bit     ENSCSIRST       0x20
+       bit     ENPHASEMIS      0x10
+       bit     ENBUSFREE       0x08
+       bit     ENSCSIPERR      0x04
+       bit     ENPHASECHG      0x02
+       bit     ENREQINIT       0x01
+}
+
+/*
+ * SCSI Data Bus (High) (p. 3-29)
+ * This register reads data on the SCSI Data bus directly.
+ */
+register SCSIBUSL {
+       address                 0x012
+       access_mode RW
+}
+
+register SCSIBUSH {
+       address                 0x013
+       access_mode RW
+}
+
+/*
+ * SCSI/Host Address (p. 3-30)
+ * These registers hold the host address for the byte about to be
+ * transferred on the SCSI bus.  They are counted up in the same
+ * manner as STCNT is counted down.  SHADDR should always be used
+ * to determine the address of the last byte transferred since HADDR
+ * can be skewed by write ahead.
+ */
+register SHADDR {
+       address                 0x014
+       size    4
+       access_mode RO
+}
+
+/*
+ * Selection Timeout Timer (p. 3-30)
+ */
+register SELTIMER {
+       address                 0x018
+       access_mode RW
+       bit     STAGE6          0x20
+       bit     STAGE5          0x10
+       bit     STAGE4          0x08
+       bit     STAGE3          0x04
+       bit     STAGE2          0x02
+       bit     STAGE1          0x01
+       alias   TARGIDIN
+}
+
+/*
+ * Selection/Reselection ID (p. 3-31)
+ * Upper four bits are the device id.  The ONEBIT is set when the re/selecting
+ * device did not set its own ID.
+ */
+register SELID {
+       address                 0x019
+       access_mode RW
+       mask    SELID_MASK      0xf0
+       bit     ONEBIT          0x08
+}
+
+register SCAMCTL {
+       address                 0x01a
+       access_mode RW
+       bit     ENSCAMSELO      0x80
+       bit     CLRSCAMSELID    0x40
+       bit     ALTSTIM         0x20
+       bit     DFLTTID         0x10
+       mask    SCAMLVL         0x03
+}
+
+/*
+ * Target Mode Selecting in ID bitmask (aic7890/91/96/97)
+ */
+register TARGID {
+       address                 0x01b
+       size                    2
+       access_mode RW
+}
+
+/*
+ * Serial Port I/O Cabability register (p. 4-95 aic7860 Data Book)
+ * Indicates if external logic has been attached to the chip to
+ * perform the tasks of accessing a serial eeprom, testing termination
+ * strength, and performing cable detection.  On the aic7860, most of
+ * these features are handled on chip, but on the aic7855 an attached
+ * aic3800 does the grunt work.
+ */
+register SPIOCAP {
+       address                 0x01b
+       access_mode RW
+       bit     SOFT1           0x80
+       bit     SOFT0           0x40
+       bit     SOFTCMDEN       0x20    
+       bit     EXT_BRDCTL      0x10    /* External Board control */
+       bit     SEEPROM         0x08    /* External serial eeprom logic */
+       bit     EEPROM          0x04    /* Writable external BIOS ROM */
+       bit     ROM             0x02    /* Logic for accessing external ROM */
+       bit     SSPIOCPS        0x01    /* Termination and cable detection */
+}
+
+register BRDCTL        {
+       address                 0x01d
+       bit     BRDDAT7         0x80
+       bit     BRDDAT6         0x40
+       bit     BRDDAT5         0x20
+       bit     BRDSTB          0x10
+       bit     BRDCS           0x08
+       bit     BRDRW           0x04
+       bit     BRDCTL1         0x02
+       bit     BRDCTL0         0x01
+       /* 7890 Definitions */
+       bit     BRDDAT4         0x10
+       bit     BRDDAT3         0x08
+       bit     BRDDAT2         0x04
+       bit     BRDRW_ULTRA2    0x02
+       bit     BRDSTB_ULTRA2   0x01
+}
+
+/*
+ * Serial EEPROM Control (p. 4-92 in 7870 Databook)
+ * Controls the reading and writing of an external serial 1-bit
+ * EEPROM Device.  In order to access the serial EEPROM, you must
+ * first set the SEEMS bit that generates a request to the memory
+ * port for access to the serial EEPROM device.  When the memory
+ * port is not busy servicing another request, it reconfigures
+ * to allow access to the serial EEPROM.  When this happens, SEERDY
+ * gets set high to verify that the memory port access has been
+ * granted.  
+ *
+ * After successful arbitration for the memory port, the SEECS bit of 
+ * the SEECTL register is connected to the chip select.  The SEECK, 
+ * SEEDO, and SEEDI are connected to the clock, data out, and data in 
+ * lines respectively.  The SEERDY bit of SEECTL is useful in that it 
+ * gives us an 800 nsec timer.  After a write to the SEECTL register, 
+ * the SEERDY goes high 800 nsec later.  The one exception to this is 
+ * when we first request access to the memory port.  The SEERDY goes 
+ * high to signify that access has been granted and, for this case, has 
+ * no implied timing.
+ *
+ * See 93cx6.c for detailed information on the protocol necessary to 
+ * read the serial EEPROM.
+ */
+register SEECTL {
+       address                 0x01e
+       bit     EXTARBACK       0x80
+       bit     EXTARBREQ       0x40
+       bit     SEEMS           0x20
+       bit     SEERDY          0x10
+       bit     SEECS           0x08
+       bit     SEECK           0x04
+       bit     SEEDO           0x02
+       bit     SEEDI           0x01
+}
+/*
+ * SCSI Block Control (p. 3-32)
+ * Controls Bus type and channel selection.  In a twin channel configuration
+ * addresses 0x00-0x1e are gated to the appropriate channel based on this
+ * register.  SELWIDE allows for the coexistence of 8bit and 16bit devices
+ * on a wide bus.
+ */
+register SBLKCTL {
+       address                 0x01f
+       access_mode RW
+       bit     DIAGLEDEN       0x80    /* Aic78X0 only */
+       bit     DIAGLEDON       0x40    /* Aic78X0 only */
+       bit     AUTOFLUSHDIS    0x20
+       bit     SELBUSB         0x08
+       bit     ENAB40          0x08    /* LVD transceiver active */
+       bit     ENAB20          0x04    /* SE/HVD transceiver active */
+       bit     SELWIDE         0x02
+       bit     XCVR            0x01    /* External transceiver active */
+}
+
+/*
+ * Sequencer Control (p. 3-33)
+ * Error detection mode and speed configuration
+ */
+register SEQCTL {
+       address                 0x060
+       access_mode RW
+       bit     PERRORDIS       0x80
+       bit     PAUSEDIS        0x40
+       bit     FAILDIS         0x20
+       bit     FASTMODE        0x10
+       bit     BRKADRINTEN     0x08
+       bit     STEP            0x04
+       bit     SEQRESET        0x02
+       bit     LOADRAM         0x01
+}
+
+/*
+ * Sequencer RAM Data (p. 3-34)
+ * Single byte window into the Scratch Ram area starting at the address
+ * specified by SEQADDR0 and SEQADDR1.  To write a full word, simply write
+ * four bytes in succession.  The SEQADDRs will increment after the most
+ * significant byte is written
+ */
+register SEQRAM {
+       address                 0x061
+       access_mode RW
+}
+
+/*
+ * Sequencer Address Registers (p. 3-35)
+ * Only the first bit of SEQADDR1 holds addressing information
+ */
+register SEQADDR0 {
+       address                 0x062
+       access_mode RW
+}
+
+register SEQADDR1 {
+       address                 0x063
+       access_mode RW
+       mask    SEQADDR1_MASK   0x01
+}
+
+/*
+ * Accumulator
+ * We cheat by passing arguments in the Accumulator up to the kernel driver
+ */
+register ACCUM {
+       address                 0x064
+       access_mode RW
+       accumulator
+}
+
+register SINDEX        {
+       address                 0x065
+       access_mode RW
+       sindex
+}
+
+register DINDEX {
+       address                 0x066
+       access_mode RW
+}
+
+register ALLONES {
+       address                 0x069
+       access_mode RO
+       allones
+}
+
+register ALLZEROS {
+       address                 0x06a
+       access_mode RO
+       allzeros
+}
+
+register NONE {
+       address                 0x06a
+       access_mode WO
+       none
+}
+
+register FLAGS {
+       address                 0x06b
+       access_mode RO
+       bit     ZERO            0x02
+       bit     CARRY           0x01
+}
+
+register SINDIR        {
+       address                 0x06c
+       access_mode RO
+}
+
+register DINDIR         {
+       address                 0x06d
+       access_mode WO
+}
+
+register FUNCTION1 {
+       address                 0x06e
+       access_mode RW
+}
+
+register STACK {
+       address                 0x06f
+       access_mode RO
+}
+
+/*
+ * Board Control (p. 3-43)
+ */
+register BCTL {
+       address                 0x084
+       access_mode RW
+       bit     ACE             0x08
+       bit     ENABLE          0x01
+}
+
+/*
+ * On the aic78X0 chips, Board Control is replaced by the DSCommand
+ * register (p. 4-64)
+ */
+register DSCOMMAND0 {
+       address                 0x084
+       access_mode RW
+       bit     CACHETHEN       0x80    /* Cache Threshold enable */
+       bit     DPARCKEN        0x40    /* Data Parity Check Enable */
+       bit     MPARCKEN        0x20    /* Memory Parity Check Enable */
+       bit     EXTREQLCK       0x10    /* External Request Lock */
+       /* aic7890/91/96/97 only */
+       bit     INTSCBRAMSEL    0x08    /* Internal SCB RAM Select */
+       bit     RAMPS           0x04    /* External SCB RAM Present */
+       bit     USCBSIZE32      0x02    /* Use 32byte SCB Page Size */
+       bit     CIOPARCKEN      0x01    /* Internal bus parity error enable */
+}
+
+register DSCOMMAND1 {
+       address                 0x085
+       access_mode RW
+       mask    DSLATT          0xfc    /* PCI latency timer (non-ultra2) */
+       bit     HADDLDSEL1      0x02    /* Host Address Load Select Bits */
+       bit     HADDLDSEL0      0x01
+}
+
+/*
+ * Bus On/Off Time (p. 3-44) aic7770 only
+ */
+register BUSTIME {
+       address                 0x085
+       access_mode RW
+       mask    BOFF            0xf0
+       mask    BON             0x0f
+}
+
+/*
+ * Bus Speed (p. 3-45) aic7770 only
+ */
+register BUSSPD {
+       address                 0x086
+       access_mode RW
+       mask    DFTHRSH         0xc0
+       mask    STBOFF          0x38
+       mask    STBON           0x07
+       mask    DFTHRSH_100     0xc0
+       mask    DFTHRSH_75      0x80
+}
+
+/* aic7850/55/60/70/80/95 only */
+register DSPCISTATUS {
+       address                 0x086
+       mask    DFTHRSH_100     0xc0
+}
+
+/* aic7890/91/96/97 only */
+register HS_MAILBOX {
+       address                 0x086
+       mask    HOST_MAILBOX    0xF0
+       mask    SEQ_MAILBOX     0x0F
+       mask    HOST_TQINPOS    0x80    /* Boundary at either 0 or 128 */
+}
+
+const  HOST_MAILBOX_SHIFT      4
+const  SEQ_MAILBOX_SHIFT       0
+
+/*
+ * Host Control (p. 3-47) R/W
+ * Overall host control of the device.
+ */
+register HCNTRL {
+       address                 0x087
+       access_mode RW
+       bit     POWRDN          0x40
+       bit     SWINT           0x10
+       bit     IRQMS           0x08
+       bit     PAUSE           0x04
+       bit     INTEN           0x02
+       bit     CHIPRST         0x01
+       bit     CHIPRSTACK      0x01
+}
+
+/*
+ * Host Address (p. 3-48)
+ * This register contains the address of the byte about
+ * to be transferred across the host bus.
+ */
+register HADDR {
+       address                 0x088
+       size    4
+       access_mode RW
+}
+
+register HCNT {
+       address                 0x08c
+       size    3
+       access_mode RW
+}
+
+/*
+ * SCB Pointer (p. 3-49)
+ * Gate one of the SCBs into the SCBARRAY window.
+ */
+register SCBPTR {
+       address                 0x090
+       access_mode RW
+}
+
+/*
+ * Interrupt Status (p. 3-50)
+ * Status for system interrupts
+ */
+register INTSTAT {
+       address                 0x091
+       access_mode RW
+       bit     BRKADRINT 0x08
+       bit     SCSIINT   0x04
+       bit     CMDCMPLT  0x02
+       bit     SEQINT    0x01
+       mask    BAD_PHASE       SEQINT          /* unknown scsi bus phase */
+       mask    SEND_REJECT     0x10|SEQINT     /* sending a message reject */
+       mask    NO_IDENT        0x20|SEQINT     /* no IDENTIFY after reconnect*/
+       mask    NO_MATCH        0x30|SEQINT     /* no cmd match for reconnect */
+       mask    IGN_WIDE_RES    0x40|SEQINT     /* Complex IGN Wide Res Msg */
+       mask    PDATA_REINIT    0x50|SEQINT     /*
+                                                * Returned to data phase
+                                                * that requires data
+                                                * transfer pointers to be
+                                                * recalculated from the
+                                                * transfer residual.
+                                                */
+       mask    HOST_MSG_LOOP   0x60|SEQINT     /*
+                                                * The bus is ready for the
+                                                * host to perform another
+                                                * message transaction.  This
+                                                * mechanism is used for things
+                                                * like sync/wide negotiation
+                                                * that require a kernel based
+                                                * message state engine.
+                                                */
+       mask    BAD_STATUS      0x70|SEQINT     /* Bad status from target */
+       mask    PERR_DETECTED   0x80|SEQINT     /*
+                                                * Either the phase_lock
+                                                * or inb_next routine has
+                                                * noticed a parity error.
+                                                */
+       mask    DATA_OVERRUN    0x90|SEQINT     /*
+                                                * Target attempted to write
+                                                * beyond the bounds of its
+                                                * command.
+                                                */
+       mask    MKMSG_FAILED    0xa0|SEQINT     /*
+                                                * Target completed command
+                                                * without honoring our ATN
+                                                * request to issue a message. 
+                                                */
+       mask    MISSED_BUSFREE  0xb0|SEQINT     /*
+                                                * The sequencer never saw
+                                                * the bus go free after
+                                                * either a command complete
+                                                * or disconnect message.
+                                                */
+       mask    SCB_MISMATCH    0xc0|SEQINT     /*
+                                                * Downloaded SCB's tag does
+                                                * not match the entry we
+                                                * intended to download.
+                                                */
+       mask    NO_FREE_SCB     0xd0|SEQINT     /*
+                                                * get_free_or_disc_scb failed.
+                                                */
+       mask    OUT_OF_RANGE    0xe0|SEQINT
+
+       mask    SEQINT_MASK     0xf0|SEQINT     /* SEQINT Status Codes */
+       mask    INT_PEND  (BRKADRINT|SEQINT|SCSIINT|CMDCMPLT)
+}
+
+/*
+ * Hard Error (p. 3-53)
+ * Reporting of catastrophic errors.  You usually cannot recover from
+ * these without a full board reset.
+ */
+register ERROR {
+       address                 0x092
+       access_mode RO
+       bit     CIOPARERR       0x80    /* Ultra2 only */
+       bit     PCIERRSTAT      0x40    /* PCI only */
+       bit     MPARERR         0x20    /* PCI only */
+       bit     DPARERR         0x10    /* PCI only */
+       bit     SQPARERR        0x08
+       bit     ILLOPCODE       0x04
+       bit     ILLSADDR        0x02
+       bit     ILLHADDR        0x01
+}
+
+/*
+ * Clear Interrupt Status (p. 3-52)
+ */
+register CLRINT {
+       address                 0x092
+       access_mode WO
+       bit     CLRPARERR       0x10    /* PCI only */
+       bit     CLRBRKADRINT    0x08
+       bit     CLRSCSIINT      0x04
+       bit     CLRCMDINT       0x02
+       bit     CLRSEQINT       0x01
+}
+
+register DFCNTRL {
+       address                 0x093
+       access_mode RW
+       bit     PRELOADEN       0x80    /* aic7890 only */
+       bit     WIDEODD         0x40
+       bit     SCSIEN          0x20
+       bit     SDMAEN          0x10
+       bit     SDMAENACK       0x10
+       bit     HDMAEN          0x08
+       bit     HDMAENACK       0x08
+       bit     DIRECTION       0x04
+       bit     FIFOFLUSH       0x02
+       bit     FIFORESET       0x01
+}
+
+register DFSTATUS {
+       address                 0x094
+       access_mode RO
+       bit     PRELOAD_AVAIL   0x80
+       bit     DFCACHETH       0x40
+       bit     FIFOQWDEMP      0x20
+       bit     MREQPEND        0x10
+       bit     HDONE           0x08
+       bit     DFTHRESH        0x04
+       bit     FIFOFULL        0x02
+       bit     FIFOEMP         0x01
+}
+
+register DFWADDR {
+       address                 0x95
+       access_mode RW
+}
+
+register DFRADDR {
+       address                 0x97
+       access_mode RW
+}
+
+register DFDAT {
+       address                 0x099
+       access_mode RW
+}
+
+/*
+ * SCB Auto Increment (p. 3-59)
+ * Byte offset into the SCB Array and an optional bit to allow auto
+ * incrementing of the address during download and upload operations
+ */
+register SCBCNT {
+       address                 0x09a
+       access_mode RW
+       bit     SCBAUTO         0x80
+       mask    SCBCNT_MASK     0x1f
+}
+
+/*
+ * Queue In FIFO (p. 3-60)
+ * Input queue for queued SCBs (commands that the seqencer has yet to start)
+ */
+register QINFIFO {
+       address                 0x09b
+       access_mode RW
+}
+
+/*
+ * Queue In Count (p. 3-60)
+ * Number of queued SCBs
+ */
+register QINCNT        {
+       address                 0x09c
+       access_mode RO
+}
+
+/*
+ * Queue Out FIFO (p. 3-61)
+ * Queue of SCBs that have completed and await the host
+ */
+register QOUTFIFO {
+       address                 0x09d
+       access_mode WO
+}
+
+register CRCCONTROL1 {
+       address                 0x09d
+       access_mode RW
+       bit     CRCONSEEN               0x80
+       bit     CRCVALCHKEN             0x40
+       bit     CRCENDCHKEN             0x20
+       bit     CRCREQCHKEN             0x10
+       bit     TARGCRCENDEN            0x08
+       bit     TARGCRCCNTEN            0x04
+}
+
+
+/*
+ * Queue Out Count (p. 3-61)
+ * Number of queued SCBs in the Out FIFO
+ */
+register QOUTCNT {
+       address                 0x09e
+       access_mode RO
+}
+
+register SCSIPHASE {
+       address                 0x09e
+       access_mode RO
+       bit     STATUS_PHASE    0x20
+       bit     COMMAND_PHASE   0x10
+       bit     MSG_IN_PHASE    0x08
+       bit     MSG_OUT_PHASE   0x04
+       bit     DATA_IN_PHASE   0x02
+       bit     DATA_OUT_PHASE  0x01
+       mask    DATA_PHASE_MASK 0x03
+}
+
+/*
+ * Special Function
+ */
+register SFUNCT {
+       address                 0x09f
+       access_mode RW
+       bit     ALT_MODE        0x80
+}
+
+/*
+ * SCB Definition (p. 5-4)
+ */
+scb {
+       address         0x0a0
+       size            64
+
+       SCB_CDB_PTR {
+               size    4
+               alias   SCB_RESIDUAL_DATACNT
+               alias   SCB_CDB_STORE
+       }
+       SCB_RESIDUAL_SGPTR {
+               size    4
+       }
+       SCB_SCSI_STATUS {
+               size    1
+       }
+       SCB_TARGET_PHASES {
+               size    1
+       }
+       SCB_TARGET_DATA_DIR {
+               size    1
+       }
+       SCB_TARGET_ITAG {
+               size    1
+       }
+       SCB_DATAPTR {
+               size    4
+       }
+       SCB_DATACNT {
+               /*
+                * The last byte is really the high address bits for
+                * the data address.
+                */
+               size    4
+               bit     SG_LAST_SEG             0x80    /* In the fourth byte */
+               mask    SG_HIGH_ADDR_BITS       0x7F    /* In the fourth byte */
+       }
+       SCB_SGPTR {
+               size    4
+               bit     SG_RESID_VALID  0x04    /* In the first byte */
+               bit     SG_FULL_RESID   0x02    /* In the first byte */
+               bit     SG_LIST_NULL    0x01    /* In the first byte */
+       }
+       SCB_CONTROL {
+               size    1
+               bit     TARGET_SCB                      0x80
+               bit     DISCENB                         0x40
+               bit     TAG_ENB                         0x20
+               bit     MK_MESSAGE                      0x10
+               bit     ULTRAENB                        0x08
+               bit     DISCONNECTED                    0x04
+               mask    SCB_TAG_TYPE                    0x03
+       }
+       SCB_SCSIID {
+               size    1
+               bit     TWIN_CHNLB                      0x80
+               mask    TWIN_TID                        0x70
+               mask    TID                             0xf0
+               mask    OID                             0x0f
+       }
+       SCB_LUN {
+               mask    LID                             0xff
+               size    1
+       }
+       SCB_TAG {
+               size    1
+       }
+       SCB_CDB_LEN {
+               size    1
+       }
+       SCB_SCSIRATE {
+               size    1
+       }
+       SCB_SCSIOFFSET {
+               size    1
+       }
+       SCB_NEXT {
+               size    1
+       }
+       SCB_64_SPARE {
+               size    16
+       }
+       SCB_64_BTT {
+               size    16
+       }
+}
+
+const  SCB_UPLOAD_SIZE         32
+const  SCB_DOWNLOAD_SIZE       32
+const  SCB_DOWNLOAD_SIZE_64    48
+
+const  SG_SIZEOF       0x08            /* sizeof(struct ahc_dma) */
+
+/* --------------------- AHA-2840-only definitions -------------------- */
+
+register SEECTL_2840 {
+       address                 0x0c0
+       access_mode RW
+       bit     CS_2840         0x04
+       bit     CK_2840         0x02
+       bit     DO_2840         0x01
+}
+
+register STATUS_2840 {
+       address                 0x0c1
+       access_mode RW
+       bit     EEPROM_TF       0x80
+       mask    BIOS_SEL        0x60
+       mask    ADSEL           0x1e
+       bit     DI_2840         0x01
+}
+
+/* --------------------- AIC-7870-only definitions -------------------- */
+
+register CCHADDR {
+       address                 0x0E0
+       size 8
+}
+
+register CCHCNT {
+       address                 0x0E8
+}
+
+register CCSGRAM {
+       address                 0x0E9
+}
+
+register CCSGADDR {
+       address                 0x0EA
+}
+
+register CCSGCTL {
+       address                 0x0EB
+       bit     CCSGDONE        0x80
+       bit     CCSGEN          0x08
+       bit     SG_FETCH_NEEDED 0x02    /* Bit used for software state */
+       bit     CCSGRESET       0x01
+}
+
+register CCSCBCNT {
+       address                 0xEF
+}
+
+register CCSCBCTL {
+       address                 0x0EE
+       bit     CCSCBDONE       0x80
+       bit     ARRDONE         0x40    /* SCB Array prefetch done */
+       bit     CCARREN         0x10
+       bit     CCSCBEN         0x08
+       bit     CCSCBDIR        0x04
+       bit     CCSCBRESET      0x01
+}
+
+register CCSCBADDR {
+       address                 0x0ED
+}
+
+register CCSCBRAM {
+       address                 0xEC
+}
+
+/*
+ * SCB bank address (7895/7896/97 only)
+ */
+register SCBBADDR {
+       address                 0x0F0
+       access_mode RW
+}
+
+register CCSCBPTR {
+       address                 0x0F1
+}
+
+register HNSCB_QOFF {
+       address                 0x0F4
+}
+
+register SNSCB_QOFF {
+       address                 0x0F6
+}
+
+register SDSCB_QOFF {
+       address                 0x0F8
+}
+
+register QOFF_CTLSTA {
+       address                 0x0FA
+       bit     SCB_AVAIL       0x40
+       bit     SNSCB_ROLLOVER  0x20
+       bit     SDSCB_ROLLOVER  0x10
+       mask    SCB_QSIZE       0x07
+       mask    SCB_QSIZE_256   0x06
+}
+
+register DFF_THRSH {
+       address                 0x0FB
+       mask    WR_DFTHRSH      0x70
+       mask    RD_DFTHRSH      0x07
+       mask    RD_DFTHRSH_MIN  0x00
+       mask    RD_DFTHRSH_25   0x01
+       mask    RD_DFTHRSH_50   0x02
+       mask    RD_DFTHRSH_63   0x03
+       mask    RD_DFTHRSH_75   0x04
+       mask    RD_DFTHRSH_85   0x05
+       mask    RD_DFTHRSH_90   0x06
+       mask    RD_DFTHRSH_MAX  0x07
+       mask    WR_DFTHRSH_MIN  0x00
+       mask    WR_DFTHRSH_25   0x10
+       mask    WR_DFTHRSH_50   0x20
+       mask    WR_DFTHRSH_63   0x30
+       mask    WR_DFTHRSH_75   0x40
+       mask    WR_DFTHRSH_85   0x50
+       mask    WR_DFTHRSH_90   0x60
+       mask    WR_DFTHRSH_MAX  0x70
+}
+
+register SG_CACHE_PRE {
+       access_mode WO
+       address                 0x0fc
+       mask    SG_ADDR_MASK    0xf8
+       bit     ODD_SEG         0x04
+       bit     LAST_SEG        0x02
+       bit     LAST_SEG_DONE   0x01
+}
+
+register SG_CACHE_SHADOW {
+       access_mode RO
+       address                 0x0fc
+       mask    SG_ADDR_MASK    0xf8
+       bit     ODD_SEG         0x04
+       bit     LAST_SEG        0x02
+       bit     LAST_SEG_DONE   0x01
+}
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+/* These offsets are either to values that are initialized by the board's
+ * BIOS or are specified by the sequencer code.
+ *
+ * The host adapter card (at least the BIOS) uses 20-2f for SCSI
+ * device information, 32-33 and 5a-5f as well. As it turns out, the
+ * BIOS trashes 20-2f, writing the synchronous negotiation results
+ * on top of the BIOS values, so we re-use those for our per-target
+ * scratchspace (actually a value that can be copied directly into
+ * SCSIRATE).  The kernel driver will enable synchronous negotiation
+ * for all targets that have a value other than 0 in the lower four
+ * bits of the target scratch space.  This should work regardless of
+ * whether the bios has been installed.
+ */
+
+scratch_ram {
+       address         0x020
+       size            58
+
+       /*
+        * 1 byte per target starting at this address for configuration values
+        */
+       BUSY_TARGETS {
+               alias           TARG_SCSIRATE
+               size            16
+       }
+       /*
+        * Bit vector of targets that have ULTRA enabled as set by
+        * the BIOS.  The Sequencer relies on a per-SCB field to
+        * control whether to enable Ultra transfers or not.  During
+        * initialization, we read this field and reuse it for 2
+        * entries in the busy target table.
+        */
+       ULTRA_ENB {
+               alias           CMDSIZE_TABLE
+               size            2
+       }
+       /*
+        * Bit vector of targets that have disconnection disabled as set by
+        * the BIOS.  The Sequencer relies in a per-SCB field to control the
+        * disconnect priveldge.  During initialization, we read this field
+        * and reuse it for 2 entries in the busy target table.
+        */
+       DISC_DSB {
+               size            2
+       }
+       CMDSIZE_TABLE_TAIL {
+               size            4
+       }
+       /*
+        * Partial transfer past cacheline end to be
+        * transferred using an extra S/G.
+        */
+       MWI_RESIDUAL {
+               size            1
+       }
+       /*
+        * SCBID of the next SCB to be started by the controller.
+        */
+       NEXT_QUEUED_SCB {
+               size            1
+       }
+       /*
+        * Single byte buffer used to designate the type or message
+        * to send to a target.
+        */
+       MSG_OUT {
+               size            1
+       }
+       /* Parameters for DMA Logic */
+       DMAPARAMS {
+               size            1
+               bit     PRELOADEN       0x80
+               bit     WIDEODD         0x40
+               bit     SCSIEN          0x20
+               bit     SDMAEN          0x10
+               bit     SDMAENACK       0x10
+               bit     HDMAEN          0x08
+               bit     HDMAENACK       0x08
+               bit     DIRECTION       0x04    /* Set indicates PCI->SCSI */
+               bit     FIFOFLUSH       0x02
+               bit     FIFORESET       0x01
+       }
+       SEQ_FLAGS {
+               size            1
+               bit     IDENTIFY_SEEN           0x80
+               bit     TARGET_CMD_IS_TAGGED    0x40
+               bit     DPHASE                  0x20
+               /* Target flags */
+               bit     TARG_CMD_PENDING        0x10
+               bit     CMDPHASE_PENDING        0x08
+               bit     DPHASE_PENDING          0x04
+               bit     SPHASE_PENDING          0x02
+               bit     NO_DISCONNECT           0x01
+       }
+       /*
+        * Temporary storage for the
+        * target/channel/lun of a
+        * reconnecting target
+        */
+       SAVED_SCSIID {
+               size            1
+       }
+       SAVED_LUN {
+               size            1
+       }
+       /*
+        * The last bus phase as seen by the sequencer. 
+        */
+       LASTPHASE {
+               size            1
+               bit     CDI             0x80
+               bit     IOI             0x40
+               bit     MSGI            0x20
+               mask    PHASE_MASK      CDI|IOI|MSGI
+               mask    P_DATAOUT       0x00
+               mask    P_DATAIN        IOI
+               mask    P_COMMAND       CDI
+               mask    P_MESGOUT       CDI|MSGI
+               mask    P_STATUS        CDI|IOI
+               mask    P_MESGIN        CDI|IOI|MSGI
+               mask    P_BUSFREE       0x01
+       }
+       /*
+        * head of list of SCBs awaiting
+        * selection
+        */
+       WAITING_SCBH {
+               size            1
+       }
+       /*
+        * head of list of SCBs that are
+        * disconnected.  Used for SCB
+        * paging.
+        */
+       DISCONNECTED_SCBH {
+               size            1
+       }
+       /*
+        * head of list of SCBs that are
+        * not in use.  Used for SCB paging.
+        */
+       FREE_SCBH {
+               size            1
+       }
+       /*
+        * head of list of SCBs that have
+        * completed but have not been
+        * put into the qoutfifo.
+        */
+       COMPLETE_SCBH {
+               size            1
+       }
+       /*
+        * Address of the hardware scb array in the host.
+        */
+       HSCB_ADDR {
+               size            4
+       }
+       /*
+        * Base address of our shared data with the kernel driver in host
+        * memory.  This includes the qoutfifo and target mode
+        * incoming command queue.
+        */
+       SHARED_DATA_ADDR {
+               size            4
+       }
+       KERNEL_QINPOS {
+               size            1
+       }
+       QINPOS {
+               size            1
+       }
+       QOUTPOS {
+               size            1
+       }
+       /*
+        * Kernel and sequencer offsets into the queue of
+        * incoming target mode command descriptors.  The
+        * queue is full when the KERNEL_TQINPOS == TQINPOS.
+        */
+       KERNEL_TQINPOS {
+               size            1
+       }
+       TQINPOS {                
+               size            1
+       }
+       ARG_1 {
+               size            1
+               mask    SEND_MSG                0x80
+               mask    SEND_SENSE              0x40
+               mask    SEND_REJ                0x20
+               mask    MSGOUT_PHASEMIS         0x10
+               mask    EXIT_MSG_LOOP           0x08
+               mask    CONT_MSG_LOOP           0x04
+               mask    CONT_TARG_SESSION       0x02
+               alias   RETURN_1
+       }
+       ARG_2 {
+               size            1
+               alias   RETURN_2
+       }
+
+       /*
+        * Snapshot of MSG_OUT taken after each message is sent.
+        */
+       LAST_MSG {
+               size            1
+       }
+
+       /*
+        * Sequences the kernel driver has okayed for us.  This allows
+        * the driver to do things like prevent initiator or target
+        * operations.
+        */
+       SCSISEQ_TEMPLATE {
+               size            1
+               bit     ENSELO          0x40
+               bit     ENSELI          0x20
+               bit     ENRSELI         0x10
+               bit     ENAUTOATNO      0x08
+               bit     ENAUTOATNI      0x04
+               bit     ENAUTOATNP      0x02
+       }
+
+       /*
+        * Track whether the transfer byte count for
+        * the current data phase is odd.
+        */
+       DATA_COUNT_ODD {
+               size            1
+       }
+}
+
+scratch_ram {
+       address         0x056
+       size            4
+       /*
+        * These scratch ram locations are initialized by the 274X BIOS.
+        * We reuse them after capturing the BIOS settings during
+        * initialization.
+        */
+
+       /*
+        * The initiator specified tag for this target mode transaction.
+        */
+       HA_274_BIOSGLOBAL {
+               size    1
+               bit     HA_274_EXTENDED_TRANS   0x01
+               alias   INITIATOR_TAG
+       }
+
+       SEQ_FLAGS2 {
+               size    1
+               bit     SCB_DMA                 0x01
+               bit     TARGET_MSG_PENDING      0x02
+       }
+}
+
+scratch_ram {
+       address         0x05a
+       size            6
+       /*
+        * These are reserved registers in the card's scratch ram on the 2742.
+        * The EISA configuraiton chip is mapped here.  On Rev E. of the
+        * aic7770, the sequencer can use this area for scratch, but the
+        * host cannot directly access these registers.  On later chips, this
+        * area can be read and written by both the host and the sequencer.
+        * Even on later chips, many of these locations are initialized by
+        * the BIOS.
+        */
+       SCSICONF {
+               size            1
+               bit     TERM_ENB        0x80
+               bit     RESET_SCSI      0x40
+               bit     ENSPCHK         0x20
+               mask    HSCSIID         0x07    /* our SCSI ID */
+               mask    HWSCSIID        0x0f    /* our SCSI ID if Wide Bus */
+       }
+       INTDEF {
+               address         0x05c
+               size            1
+               bit     EDGE_TRIG       0x80
+               mask    VECTOR          0x0f
+       }
+       HOSTCONF {
+               address         0x05d
+               size            1
+       }
+       HA_274_BIOSCTRL {
+               address         0x05f
+               size            1
+               mask    BIOSMODE                0x30
+               mask    BIOSDISABLED            0x30    
+               bit     CHANNEL_B_PRIMARY       0x08
+       }
+}
+
+scratch_ram {
+       address         0x070
+       size            16
+
+       /*
+        * Per target SCSI offset values for Ultra2 controllers.
+        */
+       TARG_OFFSET {
+               size            16
+       }
+}
+
+const TID_SHIFT                4
+const SCB_LIST_NULL    0xff
+const TARGET_CMD_CMPLT 0xfe
+
+const CCSGADDR_MAX     0x80
+const CCSGRAM_MAXSEGS  16
+
+/* WDTR Message values */
+const BUS_8_BIT                        0x00
+const BUS_16_BIT               0x01
+const BUS_32_BIT               0x02
+
+/* Offset maximums */
+const MAX_OFFSET_8BIT          0x0f
+const MAX_OFFSET_16BIT         0x08
+const MAX_OFFSET_ULTRA2                0x7f
+const HOST_MSG                 0xff
+
+/* Target mode command processing constants */
+const CMD_GROUP_CODE_SHIFT     0x05
+
+const STATUS_BUSY              0x08
+const STATUS_QUEUE_FULL        0x28
+const TARGET_DATA_IN           1
+
+/*
+ * Downloaded (kernel inserted) constants
+ */
+/* Offsets into the SCBID array where different data is stored */
+const QOUTFIFO_OFFSET download
+const QINFIFO_OFFSET download
+const CACHESIZE_MASK download
+const INVERTED_CACHESIZE_MASK download
+const SG_PREFETCH_CNT download
+const SG_PREFETCH_ALIGN_MASK download
+const SG_PREFETCH_ADDR_MASK download
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx.seq b/xen/drivers/scsi/aic7xxx/aic7xxx.seq
new file mode 100644 (file)
index 0000000..db64c6f
--- /dev/null
@@ -0,0 +1,2365 @@
+/*
+ * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.seq,v 1.94.2.16 2002/04/29 19:36:30 gibbs Exp $
+ */
+
+VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#43 $"
+PATCH_ARG_LIST = "struct ahc_softc *ahc"
+
+#include "aic7xxx.reg"
+#include "scsi_message.h"
+
+/*
+ * A few words on the waiting SCB list:
+ * After starting the selection hardware, we check for reconnecting targets
+ * as well as for our selection to complete just in case the reselection wins
+ * bus arbitration.  The problem with this is that we must keep track of the
+ * SCB that we've already pulled from the QINFIFO and started the selection
+ * on just in case the reselection wins so that we can retry the selection at
+ * a later time.  This problem cannot be resolved by holding a single entry
+ * in scratch ram since a reconnecting target can request sense and this will
+ * create yet another SCB waiting for selection.  The solution used here is to 
+ * use byte 27 of the SCB as a psuedo-next pointer and to thread a list
+ * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB indexes, 
+ * SCB_LIST_NULL is 0xff which is out of range.  An entry is also added to
+ * this list everytime a request sense occurs or after completing a non-tagged
+ * command for which a second SCB has been queued.  The sequencer will
+ * automatically consume the entries.
+ */
+
+bus_free_sel:
+       /*
+        * Turn off the selection hardware.  We need to reset the
+        * selection request in order to perform a new selection.
+        */
+       and     SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ;
+       and     SIMODE1, ~ENBUSFREE;
+poll_for_work:
+       call    clear_target_state;
+       and     SXFRCTL0, ~SPIOEN;
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               clr     SCSIBUSL;
+       }
+       test    SCSISEQ, ENSELO jnz poll_for_selection;
+       if ((ahc->features & AHC_TWIN) != 0) {
+               xor     SBLKCTL,SELBUSB;        /* Toggle to the other bus */
+               test    SCSISEQ, ENSELO         jnz poll_for_selection;
+       }
+       cmp     WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
+poll_for_work_loop:
+       if ((ahc->features & AHC_TWIN) != 0) {
+               xor     SBLKCTL,SELBUSB;        /* Toggle to the other bus */
+       }
+       test    SSTAT0, SELDO|SELDI     jnz selection;
+test_queue:
+       /* Has the driver posted any work for us? */
+BEGIN_CRITICAL;
+       if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+               test    QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
+       } else {
+               mov     A, QINPOS;
+               cmp     KERNEL_QINPOS, A je poll_for_work_loop;
+       }
+       mov     ARG_1, NEXT_QUEUED_SCB;
+
+       /*
+        * We have at least one queued SCB now and we don't have any 
+        * SCBs in the list of SCBs awaiting selection.  Allocate a
+        * card SCB for the host's SCB and get to work on it.
+        */
+       if ((ahc->flags & AHC_PAGESCBS) != 0) {
+               mov     ALLZEROS        call    get_free_or_disc_scb;
+       } else {
+               /* In the non-paging case, the SCBID == hardware SCB index */
+               mov     SCBPTR, ARG_1;
+       }
+       or      SEQ_FLAGS2, SCB_DMA;
+END_CRITICAL;
+dma_queued_scb:
+       /*
+        * DMA the SCB from host ram into the current SCB location.
+        */
+       mvi     DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+       mov     ARG_1   call dma_scb;
+       /*
+        * Check one last time to see if this SCB was canceled
+        * before we completed the DMA operation.  If it was,
+        * the QINFIFO next pointer will not match our saved
+        * value.
+        */
+       mov     A, ARG_1;
+BEGIN_CRITICAL;
+       cmp     NEXT_QUEUED_SCB, A jne abort_qinscb;
+       if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+               cmp     SCB_TAG, A je . + 2;
+               mvi     SCB_MISMATCH call set_seqint;
+       }
+       mov     NEXT_QUEUED_SCB, SCB_NEXT;
+       mov     SCB_NEXT,WAITING_SCBH;
+       mov     WAITING_SCBH, SCBPTR;
+       if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+               mov     NONE, SNSCB_QOFF;
+       } else {
+               inc     QINPOS;
+       }
+       and     SEQ_FLAGS2, ~SCB_DMA;
+END_CRITICAL;
+start_waiting:
+       /*
+        * Start the first entry on the waiting SCB list.
+        */
+       mov     SCBPTR, WAITING_SCBH;
+       call    start_selection;
+
+poll_for_selection:
+       /*
+        * Twin channel devices cannot handle things like SELTO
+        * interrupts on the "background" channel.  So, while
+        * selecting, keep polling the current channel until
+        * either a selection or reselection occurs.
+        */
+       test    SSTAT0, SELDO|SELDI     jz poll_for_selection;
+
+selection:
+       /*
+        * We aren't expecting a bus free, so interrupt
+        * the kernel driver if it happens.
+        */
+       mvi     CLRSINT1,CLRBUSFREE;
+       if ((ahc->features & AHC_DT) == 0) {
+               or      SIMODE1, ENBUSFREE;
+       }
+
+       /*
+        * Guard against a bus free after (re)selection
+        * but prior to enabling the busfree interrupt.  SELDI
+        * and SELDO will be cleared in that case.
+        */
+       test    SSTAT0, SELDI|SELDO     jz bus_free_sel;
+       test    SSTAT0,SELDO    jnz select_out;
+select_in:
+       if ((ahc->flags & AHC_TARGETROLE) != 0) {
+               if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+                       test    SSTAT0, TARGET  jz initiator_reselect;
+               }
+               mvi     CLRSINT0, CLRSELDI;
+
+               /*
+                * We've just been selected.  Assert BSY and
+                * setup the phase for receiving messages
+                * from the target.
+                */
+               mvi     SCSISIGO, P_MESGOUT|BSYO;
+
+               /*
+                * Setup the DMA for sending the identify and
+                * command information.
+                */
+               or      SEQ_FLAGS, CMDPHASE_PENDING;
+
+               mov     A, TQINPOS;
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       mvi     DINDEX, CCHADDR;
+                       mvi     SHARED_DATA_ADDR call set_32byte_addr;
+                       mvi     CCSCBCTL, CCSCBRESET;
+               } else {
+                       mvi     DINDEX, HADDR;
+                       mvi     SHARED_DATA_ADDR call set_32byte_addr;
+                       mvi     DFCNTRL, FIFORESET;
+               }
+
+               /* Initiator that selected us */
+               and     SAVED_SCSIID, SELID_MASK, SELID;
+               /* The Target ID we were selected at */
+               if ((ahc->features & AHC_MULTI_TID) != 0) {
+                       and     A, OID, TARGIDIN;
+               } else if ((ahc->features & AHC_ULTRA2) != 0) {
+                       and     A, OID, SCSIID_ULTRA2;
+               } else {
+                       and     A, OID, SCSIID;
+               }
+               or      SAVED_SCSIID, A;
+               if ((ahc->features & AHC_TWIN) != 0) {
+                       test    SBLKCTL, SELBUSB jz . + 2;
+                       or      SAVED_SCSIID, TWIN_CHNLB;
+               }
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       mov     CCSCBRAM, SAVED_SCSIID;
+               } else {
+                       mov     DFDAT, SAVED_SCSIID;
+               }
+
+               /*
+                * If ATN isn't asserted, the target isn't interested
+                * in talking to us.  Go directly to bus free.
+                * XXX SCSI-1 may require us to assume lun 0 if
+                * ATN is false.
+                */
+               test    SCSISIGI, ATNI  jz      target_busfree;
+
+               /*
+                * Watch ATN closely now as we pull in messages from the
+                * initiator.  We follow the guidlines from section 6.5
+                * of the SCSI-2 spec for what messages are allowed when.
+                */
+               call    target_inb;
+
+               /*
+                * Our first message must be one of IDENTIFY, ABORT, or
+                * BUS_DEVICE_RESET.
+                */
+               test    DINDEX, MSG_IDENTIFYFLAG jz host_target_message_loop;
+               /* Store for host */
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       mov     CCSCBRAM, DINDEX;
+               } else {
+                       mov     DFDAT, DINDEX;
+               }
+               and     SAVED_LUN, MSG_IDENTIFY_LUNMASK, DINDEX;
+
+               /* Remember for disconnection decision */
+               test    DINDEX, MSG_IDENTIFY_DISCFLAG jnz . + 2;
+               /* XXX Honor per target settings too */
+               or      SEQ_FLAGS, NO_DISCONNECT;
+
+               test    SCSISIGI, ATNI  jz      ident_messages_done;
+               call    target_inb;
+               /*
+                * If this is a tagged request, the tagged message must
+                * immediately follow the identify.  We test for a valid
+                * tag message by seeing if it is >= MSG_SIMPLE_Q_TAG and
+                * < MSG_IGN_WIDE_RESIDUE.
+                */
+               add     A, -MSG_SIMPLE_Q_TAG, DINDEX;
+               jnc     ident_messages_done_msg_pending;
+               add     A, -MSG_IGN_WIDE_RESIDUE, DINDEX;
+               jc      ident_messages_done_msg_pending;
+
+               /* Store for host */
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       mov     CCSCBRAM, DINDEX;
+               } else {
+                       mov     DFDAT, DINDEX;
+               }
+               
+               /*
+                * If the initiator doesn't feel like providing a tag number,
+                * we've got a failed selection and must transition to bus
+                * free.
+                */
+               test    SCSISIGI, ATNI  jz      target_busfree;
+
+               /*
+                * Store the tag for the host.
+                */
+               call    target_inb;
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       mov     CCSCBRAM, DINDEX;
+               } else {
+                       mov     DFDAT, DINDEX;
+               }
+               mov     INITIATOR_TAG, DINDEX;
+               or      SEQ_FLAGS, TARGET_CMD_IS_TAGGED;
+
+ident_messages_done:
+               /* Terminate the ident list */
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       mvi     CCSCBRAM, SCB_LIST_NULL;
+               } else {
+                       mvi     DFDAT, SCB_LIST_NULL;
+               }
+               or      SEQ_FLAGS, TARG_CMD_PENDING|IDENTIFY_SEEN;
+               test    SEQ_FLAGS2, TARGET_MSG_PENDING
+                       jnz target_mesgout_pending;
+               test    SCSISIGI, ATNI jnz target_mesgout_continue;
+               jmp     target_ITloop;
+
+
+ident_messages_done_msg_pending:
+               or      SEQ_FLAGS2, TARGET_MSG_PENDING;
+               jmp     ident_messages_done;
+
+               /*
+                * Pushed message loop to allow the kernel to
+                * run it's own target mode message state engine.
+                */
+host_target_message_loop:
+               mvi     HOST_MSG_LOOP call set_seqint;
+               cmp     RETURN_1, EXIT_MSG_LOOP je target_ITloop;
+               test    SSTAT0, SPIORDY jz .;
+               jmp     host_target_message_loop;
+       }
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+/*
+ * Reselection has been initiated by a target. Make a note that we've been
+ * reselected, but haven't seen an IDENTIFY message from the target yet.
+ */
+initiator_reselect:
+       /* XXX test for and handle ONE BIT condition */
+       or      SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
+       and     SAVED_SCSIID, SELID_MASK, SELID;
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               and     A, OID, SCSIID_ULTRA2;
+       } else {
+               and     A, OID, SCSIID;
+       }
+       or      SAVED_SCSIID, A;
+       if ((ahc->features & AHC_TWIN) != 0) {
+               test    SBLKCTL, SELBUSB        jz . + 2;
+               or      SAVED_SCSIID, TWIN_CHNLB;
+       }
+       mvi     CLRSINT0, CLRSELDI;
+       jmp     ITloop;
+}
+
+abort_qinscb:
+       call    add_scb_to_free_list;
+       jmp     poll_for_work_loop;
+
+start_selection:
+       /*
+        * If bus reset interrupts have been disabled (from a previous
+        * reset), re-enable them now.  Resets are only of interest
+        * when we have outstanding transactions, so we can safely
+        * defer re-enabling the interrupt until, as an initiator,
+        * we start sending out transactions again.
+        */
+       test    SIMODE1, ENSCSIRST      jnz . + 3;
+       mvi     CLRSINT1, CLRSCSIRSTI;
+       or      SIMODE1, ENSCSIRST;
+       if ((ahc->features & AHC_TWIN) != 0) {
+               and     SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
+               test    SCB_SCSIID, TWIN_CHNLB jz . + 2;
+               or      SINDEX, SELBUSB;
+               mov     SBLKCTL,SINDEX;         /* select channel */
+       }
+initialize_scsiid:
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               mov     SCSIID_ULTRA2, SCB_SCSIID;
+       } else if ((ahc->features & AHC_TWIN) != 0) {
+               and     SCSIID, TWIN_TID|OID, SCB_SCSIID;
+       } else {
+               mov     SCSIID, SCB_SCSIID;
+       }
+       if ((ahc->flags & AHC_TARGETROLE) != 0) {
+               mov     SINDEX, SCSISEQ_TEMPLATE;
+               test    SCB_CONTROL, TARGET_SCB jz . + 2;
+               or      SINDEX, TEMODE;
+               mov     SCSISEQ, SINDEX ret;
+       } else {
+               mov     SCSISEQ, SCSISEQ_TEMPLATE ret;
+       }
+
+/*
+ * Initialize transfer settings and clear the SCSI channel.
+ * SINDEX should contain any additional bit's the client wants
+ * set in SXFRCTL0.  We also assume that the current SCB is
+ * a valid SCB for the target we wish to talk to.
+ */
+initialize_channel:
+       or      SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
+set_transfer_settings:
+       if ((ahc->features & AHC_ULTRA) != 0) {
+               test    SCB_CONTROL, ULTRAENB jz . + 2;
+               or      SXFRCTL0, FAST20;
+       } 
+       /*
+        * Initialize SCSIRATE with the appropriate value for this target.
+        */
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               bmov    SCSIRATE, SCB_SCSIRATE, 2 ret;
+       } else {
+               mov     SCSIRATE, SCB_SCSIRATE ret;
+       }
+
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
+/*
+ * We carefully toggle SPIOEN to allow us to return the 
+ * message byte we receive so it can be checked prior to
+ * driving REQ on the bus for the next byte.
+ */
+target_inb:
+       /*
+        * Drive REQ on the bus by enabling SCSI PIO.
+        */
+       or      SXFRCTL0, SPIOEN;
+       /* Wait for the byte */
+       test    SSTAT0, SPIORDY jz .;
+       /* Prevent our read from triggering another REQ */
+       and     SXFRCTL0, ~SPIOEN;
+       /* Save latched contents */
+       mov     DINDEX, SCSIDATL ret;
+}
+
+/*
+ * After the selection, remove this SCB from the "waiting SCB"
+ * list.  This is achieved by simply moving our "next" pointer into
+ * WAITING_SCBH.  Our next pointer will be set to null the next time this
+ * SCB is used, so don't bother with it now.
+ */
+select_out:
+       /* Turn off the selection hardware */
+       and     SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ;
+       mov     SCBPTR, WAITING_SCBH;
+       mov     WAITING_SCBH,SCB_NEXT;
+       mov     SAVED_SCSIID, SCB_SCSIID;
+       mov     SAVED_LUN, SCB_LUN;
+       call    initialize_channel;
+       if ((ahc->flags & AHC_TARGETROLE) != 0) {
+               test    SSTAT0, TARGET  jz initiator_select;
+
+               /*
+                * We've just re-selected an initiator.
+                * Assert BSY and setup the phase for
+                * sending our identify messages.
+                */
+               mvi     P_MESGIN|BSYO call change_phase;
+               mvi     CLRSINT0, CLRSELDO;
+
+               /*
+                * Start out with a simple identify message.
+                */
+               or      SCB_LUN, MSG_IDENTIFYFLAG call target_outb;
+
+               /*
+                * If we are the result of a tagged command, send
+                * a simple Q tag and the tag id.
+                */
+               test    SCB_CONTROL, TAG_ENB    jz . + 3;
+               mvi     MSG_SIMPLE_Q_TAG call target_outb;
+               mov     SCB_TARGET_ITAG call target_outb;
+target_synccmd:
+               /*
+                * Now determine what phases the host wants us
+                * to go through.
+                */
+               mov     SEQ_FLAGS, SCB_TARGET_PHASES;
+               
+               test    SCB_CONTROL, MK_MESSAGE jz target_ITloop;
+               mvi     P_MESGIN|BSYO call change_phase;
+               jmp     host_target_message_loop;
+target_ITloop:
+               /*
+                * Start honoring ATN signals now that
+                * we properly identified ourselves.
+                */
+               test    SCSISIGI, ATNI                  jnz target_mesgout;
+               test    SEQ_FLAGS, CMDPHASE_PENDING     jnz target_cmdphase;
+               test    SEQ_FLAGS, DPHASE_PENDING       jnz target_dphase;
+               test    SEQ_FLAGS, SPHASE_PENDING       jnz target_sphase;
+
+               /*
+                * No more work to do.  Either disconnect or not depending
+                * on the state of NO_DISCONNECT.
+                */
+               test    SEQ_FLAGS, NO_DISCONNECT jz target_disconnect; 
+               mov     RETURN_1, ALLZEROS;
+               call    complete_target_cmd;
+               cmp     RETURN_1, CONT_MSG_LOOP jne .;
+               if ((ahc->flags & AHC_PAGESCBS) != 0) {
+                       mov     ALLZEROS        call    get_free_or_disc_scb;
+               }
+               mvi     DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+               mov     SCB_TAG  call dma_scb;
+               call    set_transfer_settings;
+               jmp     target_synccmd;
+
+target_mesgout:
+               mvi     SCSISIGO, P_MESGOUT|BSYO;
+target_mesgout_continue:
+               call    target_inb;
+target_mesgout_pending:
+               /* Local Processing goes here... */
+               jmp     host_target_message_loop;
+               
+target_disconnect:
+               mvi     P_MESGIN|BSYO call change_phase;
+               test    SEQ_FLAGS, DPHASE       jz . + 2;
+               mvi     MSG_SAVEDATAPOINTER call target_outb;
+               mvi     MSG_DISCONNECT call target_outb;
+
+target_busfree_wait:
+               /* Wait for preceding I/O session to complete. */
+               test    SCSISIGI, ACKI jnz .;
+target_busfree:
+               and     SIMODE1, ~ENBUSFREE;
+               if ((ahc->features & AHC_ULTRA2) != 0) {
+                       clr     SCSIBUSL;
+               }
+               clr     SCSISIGO;
+               mvi     LASTPHASE, P_BUSFREE;
+               call    complete_target_cmd;
+               jmp     poll_for_work;
+
+target_cmdphase:
+               /*
+                * The target has dropped ATN (doesn't want to abort or BDR)
+                * and we believe this selection to be valid.  If the ring
+                * buffer for new commands is full, return busy or queue full.
+                */
+               if ((ahc->features & AHC_HS_MAILBOX) != 0) {
+                       and     A, HOST_TQINPOS, HS_MAILBOX;
+               } else {
+                       mov     A, KERNEL_TQINPOS;
+               }
+               cmp     TQINPOS, A jne tqinfifo_has_space;
+               mvi     P_STATUS|BSYO call change_phase;
+               test    SEQ_FLAGS, TARGET_CMD_IS_TAGGED jz . + 3;
+               mvi     STATUS_QUEUE_FULL call target_outb;
+               jmp     target_busfree_wait;
+               mvi     STATUS_BUSY call target_outb;
+               jmp     target_busfree_wait;
+tqinfifo_has_space:    
+               mvi     P_COMMAND|BSYO call change_phase;
+               call    target_inb;
+               mov     A, DINDEX;
+               /* Store for host */
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       mov     CCSCBRAM, A;
+               } else {
+                       mov     DFDAT, A;
+               }
+
+               /*
+                * Determine the number of bytes to read
+                * based on the command group code via table lookup.
+                * We reuse the first 8 bytes of the TARG_SCSIRATE
+                * BIOS array for this table. Count is one less than
+                * the total for the command since we've already fetched
+                * the first byte.
+                */
+               shr     A, CMD_GROUP_CODE_SHIFT;
+               add     SINDEX, CMDSIZE_TABLE, A;
+               mov     A, SINDIR;
+
+               test    A, 0xFF jz command_phase_done;
+               or      SXFRCTL0, SPIOEN;
+command_loop:
+               test    SSTAT0, SPIORDY jz .;
+               cmp     A, 1 jne . + 2;
+               and     SXFRCTL0, ~SPIOEN;      /* Last Byte */
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       mov     CCSCBRAM, SCSIDATL;
+               } else {
+                       mov     DFDAT, SCSIDATL;
+               }
+               dec     A;
+               test    A, 0xFF jnz command_loop;
+
+command_phase_done:
+               and     SEQ_FLAGS, ~CMDPHASE_PENDING;
+               jmp     target_ITloop;
+
+target_dphase:
+               /*
+                * Data phases on the bus are from the
+                * perspective of the initiator.  The dma
+                * code looks at LASTPHASE to determine the
+                * data direction of the DMA.  Toggle it for
+                * target transfers.
+                */
+               xor     LASTPHASE, IOI, SCB_TARGET_DATA_DIR;
+               or      SCB_TARGET_DATA_DIR, BSYO call change_phase;
+               jmp     p_data;
+
+target_sphase:
+               mvi     P_STATUS|BSYO call change_phase;
+               mvi     LASTPHASE, P_STATUS;
+               mov     SCB_SCSI_STATUS call target_outb;
+               /* XXX Watch for ATN or parity errors??? */
+               mvi     SCSISIGO, P_MESGIN|BSYO;
+               /* MSG_CMDCMPLT is 0, but we can't do an immediate of 0 */
+               mov     ALLZEROS call target_outb;
+               jmp     target_busfree_wait;
+       
+complete_target_cmd:
+               test    SEQ_FLAGS, TARG_CMD_PENDING     jnz . + 2;
+               mov     SCB_TAG jmp complete_post;
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       /* Set the valid byte */
+                       mvi     CCSCBADDR, 24;
+                       mov     CCSCBRAM, ALLONES;
+                       mvi     CCHCNT, 28;
+                       or      CCSCBCTL, CCSCBEN|CCSCBRESET;
+                       test    CCSCBCTL, CCSCBDONE jz .;
+                       clr     CCSCBCTL;
+               } else {
+                       /* Set the valid byte */
+                       or      DFCNTRL, FIFORESET;
+                       mvi     DFWADDR, 3; /* Third 64bit word or byte 24 */
+                       mov     DFDAT, ALLONES;
+                       mvi     28      call set_hcnt;
+                       or      DFCNTRL, HDMAEN|FIFOFLUSH;
+                       call    dma_finish;
+               }
+               inc     TQINPOS;
+               mvi     INTSTAT,CMDCMPLT ret;
+       }
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+initiator_select:
+       /*
+        * As soon as we get a successful selection, the target
+        * should go into the message out phase since we have ATN
+        * asserted.
+        */
+       mvi     MSG_OUT, MSG_IDENTIFYFLAG;
+       or      SEQ_FLAGS, IDENTIFY_SEEN;
+       mvi     CLRSINT0, CLRSELDO;
+
+       /*
+        * Main loop for information transfer phases.  Wait for the
+        * target to assert REQ before checking MSG, C/D and I/O for
+        * the bus phase.
+        */
+mesgin_phasemis:
+ITloop:
+       call    phase_lock;
+
+       mov     A, LASTPHASE;
+
+       test    A, ~P_DATAIN    jz p_data;
+       cmp     A,P_COMMAND     je p_command;
+       cmp     A,P_MESGOUT     je p_mesgout;
+       cmp     A,P_STATUS      je p_status;
+       cmp     A,P_MESGIN      je p_mesgin;
+
+       mvi     BAD_PHASE call set_seqint;
+       jmp     ITloop;                 /* Try reading the bus again. */
+
+await_busfree:
+       and     SIMODE1, ~ENBUSFREE;
+       mov     NONE, SCSIDATL;         /* Ack the last byte */
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               clr     SCSIBUSL;       /* Prevent bit leakage durint SELTO */
+       }
+       and     SXFRCTL0, ~SPIOEN;
+       test    SSTAT1,REQINIT|BUSFREE  jz .;
+       test    SSTAT1, BUSFREE jnz poll_for_work;
+       mvi     MISSED_BUSFREE call set_seqint;
+}
+       
+clear_target_state:
+       /*
+        * We assume that the kernel driver may reset us
+        * at any time, even in the middle of a DMA, so
+        * clear DFCNTRL too.
+        */
+       clr     DFCNTRL;
+       or      SXFRCTL0, CLRSTCNT|CLRCHN;
+
+       /*
+        * We don't know the target we will connect to,
+        * so default to narrow transfers to avoid
+        * parity problems.
+        */
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               bmov    SCSIRATE, ALLZEROS, 2;
+       } else {
+               clr     SCSIRATE;
+               if ((ahc->features & AHC_ULTRA) != 0) {
+                       and     SXFRCTL0, ~(FAST20);
+               }
+       }
+       mvi     LASTPHASE, P_BUSFREE;
+       /* clear target specific flags */
+       clr     SEQ_FLAGS ret;
+
+sg_advance:
+       clr     A;                      /* add sizeof(struct scatter) */
+       add     SCB_RESIDUAL_SGPTR[0],SG_SIZEOF;
+       adc     SCB_RESIDUAL_SGPTR[1],A;
+       adc     SCB_RESIDUAL_SGPTR[2],A;
+       adc     SCB_RESIDUAL_SGPTR[3],A ret;
+
+if ((ahc->features & AHC_CMD_CHAN) != 0) {
+disable_ccsgen:
+       test    CCSGCTL, CCSGEN jz return;
+       test    CCSGCTL, CCSGDONE jz .;
+disable_ccsgen_fetch_done:
+       clr     CCSGCTL;
+       test    CCSGCTL, CCSGEN jnz .;
+       ret;
+idle_loop:
+       /*
+        * Do we need any more segments for this transfer?
+        */
+       test    SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jnz return;
+
+       /* Did we just finish fetching segs? */
+       cmp     CCSGCTL, CCSGEN|CCSGDONE je idle_sgfetch_complete;
+
+       /* Are we actively fetching segments? */
+       test    CCSGCTL, CCSGEN jnz return;
+
+       /*
+        * Do we have any prefetch left???
+        */
+       cmp     CCSGADDR, SG_PREFETCH_CNT jne idle_sg_avail;
+
+       /*
+        * Need to fetch segments, but we can only do that
+        * if the command channel is completely idle.  Make
+        * sure we don't have an SCB prefetch going on.
+        */
+       test    CCSCBCTL, CCSCBEN jnz return;
+
+       /*
+        * We fetch a "cacheline aligned" and sized amount of data
+        * so we don't end up referencing a non-existant page.
+        * Cacheline aligned is in quotes because the kernel will
+        * set the prefetch amount to a reasonable level if the
+        * cacheline size is unknown.
+        */
+       mvi     CCHCNT, SG_PREFETCH_CNT;
+       and     CCHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR;
+       bmov    CCHADDR[1], SCB_RESIDUAL_SGPTR[1], 3;
+       mvi     CCSGCTL, CCSGEN|CCSGRESET ret;
+idle_sgfetch_complete:
+       call    disable_ccsgen_fetch_done;
+       and     CCSGADDR, SG_PREFETCH_ADDR_MASK, SCB_RESIDUAL_SGPTR;
+idle_sg_avail:
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               /* Does the hardware have space for another SG entry? */
+               test    DFSTATUS, PRELOAD_AVAIL jz return;
+               bmov    HADDR, CCSGRAM, 7;
+               test    HCNT[0], 0x1 jz . + 2;
+               xor     DATA_COUNT_ODD, 0x1;
+               bmov    SCB_RESIDUAL_DATACNT[3], CCSGRAM, 1;
+               if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+                       mov     SCB_RESIDUAL_DATACNT[3] call set_hhaddr;
+               }
+               call    sg_advance;
+               mov     SINDEX, SCB_RESIDUAL_SGPTR[0];
+               test    DATA_COUNT_ODD, 0x1 jz . + 2;
+               or      SINDEX, ODD_SEG;
+               test    SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 2;
+               or      SINDEX, LAST_SEG;
+               mov     SG_CACHE_PRE, SINDEX;
+               /* Load the segment */
+               or      DFCNTRL, PRELOADEN;
+       }
+       ret;
+}
+
+if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) {
+/*
+ * Calculate the trailing portion of this S/G segment that cannot
+ * be transferred using memory write and invalidate PCI transactions.  
+ * XXX Can we optimize this for PCI writes only???
+ */
+calc_mwi_residual:
+       /*
+        * If the ending address is on a cacheline boundary,
+        * there is no need for an extra segment.
+        */
+       mov     A, HCNT[0];
+       add     A, A, HADDR[0];
+       and     A, CACHESIZE_MASK;
+       test    A, 0xFF jz return;
+
+       /*
+        * If the transfer is less than a cachline,
+        * there is no need for an extra segment.
+        */
+       test    HCNT[1], 0xFF   jnz calc_mwi_residual_final;
+       test    HCNT[2], 0xFF   jnz calc_mwi_residual_final;
+       add     NONE, INVERTED_CACHESIZE_MASK, HCNT[0];
+       jnc     return;
+
+calc_mwi_residual_final:
+       mov     MWI_RESIDUAL, A;
+       not     A;
+       inc     A;
+       add     HCNT[0], A;
+       adc     HCNT[1], -1;
+       adc     HCNT[2], -1 ret;
+}
+
+p_data:
+       test    SEQ_FLAGS,IDENTIFY_SEEN jnz p_data_okay;
+       mvi     NO_IDENT jmp set_seqint;
+p_data_okay:
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               mvi     DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
+       } else {
+               mvi     DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+       }
+       test    LASTPHASE, IOI jnz . + 2;
+       or      DMAPARAMS, DIRECTION;
+       if ((ahc->features & AHC_CMD_CHAN) != 0) {
+               /* We don't have any valid S/G elements */
+               mvi     CCSGADDR, SG_PREFETCH_CNT;
+       }
+       test    SEQ_FLAGS, DPHASE       jz data_phase_initialize;
+
+       /*
+        * If we re-enter the data phase after going through another
+        * phase, our transfer location has almost certainly been
+        * corrupted by the interveining, non-data, transfers.  Ask
+        * the host driver to fix us up based on the transfer residual.
+        */
+       mvi     PDATA_REINIT    call set_seqint;
+       jmp     data_phase_loop;
+
+data_phase_initialize:
+       /* We have seen a data phase for the first time */
+       or      SEQ_FLAGS, DPHASE;
+
+       /*
+        * Initialize the DMA address and counter from the SCB.
+        * Also set SCB_RESIDUAL_SGPTR, including the LAST_SEG
+        * flag in the highest byte of the data count.  We cannot
+        * modify the saved values in the SCB until we see a save
+        * data pointers message.
+        */
+       if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+               /* The lowest address byte must be loaded last. */
+               mov     SCB_DATACNT[3] call set_hhaddr;
+       }
+       if ((ahc->features & AHC_CMD_CHAN) != 0) {
+               bmov    HADDR, SCB_DATAPTR, 7;
+               bmov    SCB_RESIDUAL_DATACNT[3], SCB_DATACNT[3], 5;
+       } else {
+               mvi     DINDEX, HADDR;
+               mvi     SCB_DATAPTR     call bcopy_7;
+               mvi     DINDEX, SCB_RESIDUAL_DATACNT + 3;
+               mvi     SCB_DATACNT + 3 call bcopy_5;
+       }
+       if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) {
+               call    calc_mwi_residual;
+       }
+       and     SCB_RESIDUAL_SGPTR[0], ~SG_FULL_RESID;
+       and     DATA_COUNT_ODD, 0x1, HCNT[0];
+
+       if ((ahc->features & AHC_ULTRA2) == 0) {
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       bmov    STCNT, HCNT, 3;
+               } else {
+                       call    set_stcnt_from_hcnt;
+               }
+       }
+
+data_phase_loop:
+       /* Guard against overruns */
+       test    SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz data_phase_inbounds;
+
+       /*
+        * Turn on `Bit Bucket' mode, wait until the target takes
+        * us to another phase, and then notify the host.
+        */
+       and     DMAPARAMS, DIRECTION;
+       mov     DFCNTRL, DMAPARAMS;
+       or      SXFRCTL1,BITBUCKET;
+       if ((ahc->features & AHC_DT) == 0) {
+               test    SSTAT1,PHASEMIS jz .;
+       } else {
+               test    SCSIPHASE, DATA_PHASE_MASK jnz .;
+       }
+       and     SXFRCTL1, ~BITBUCKET;
+       mvi     DATA_OVERRUN call set_seqint;
+       jmp     ITloop;
+
+data_phase_inbounds:
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               mov     SINDEX, SCB_RESIDUAL_SGPTR[0];
+               test    SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 2;
+               or      SINDEX, LAST_SEG;
+               test    DATA_COUNT_ODD, 0x1 jz . + 2;
+               or      SINDEX, ODD_SEG;
+               mov     SG_CACHE_PRE, SINDEX;
+               mov     DFCNTRL, DMAPARAMS;
+ultra2_dma_loop:
+               call    idle_loop;
+               /*
+                * The transfer is complete if either the last segment
+                * completes or the target changes phase.
+                */
+               test    SG_CACHE_SHADOW, LAST_SEG_DONE jnz ultra2_dmafinish;
+               if ((ahc->features & AHC_DT) == 0) {
+                       if ((ahc->flags & AHC_TARGETROLE) != 0) {
+                                /*
+                                 * As a target, we control the phases,
+                                 * so ignore PHASEMIS.
+                                 */
+                               test    SSTAT0, TARGET jnz ultra2_dma_loop;
+                       }
+                       if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+                               test    SSTAT1,PHASEMIS jz ultra2_dma_loop;
+                       }
+               } else {
+                       test    DFCNTRL, SCSIEN jnz ultra2_dma_loop;
+               }
+
+ultra2_dmafinish:
+               /*
+                * The transfer has terminated either due to a phase
+                * change, and/or the completion of the last segment.
+                * We have two goals here.  Do as much other work
+                * as possible while the data fifo drains on a read
+                * and respond as quickly as possible to the standard
+                * messages (save data pointers/disconnect and command
+                * complete) that usually follow a data phase.
+                */
+               if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) {
+                       /*
+                        * On chips with broken auto-flush, start
+                        * the flushing process now.  We'll poke
+                        * the chip from time to time to keep the
+                        * flush process going as we complete the
+                        * data phase.
+                        */
+                       or      DFCNTRL, FIFOFLUSH;
+               }
+               /*
+                * We assume that, even though data may still be
+                * transferring to the host, that the SCSI side of
+                * the DMA engine is now in a static state.  This
+                * allows us to update our notion of where we are
+                * in this transfer.
+                *
+                * If, by chance, we stopped before being able
+                * to fetch additional segments for this transfer,
+                * yet the last S/G was completely exhausted,
+                * call our idle loop until it is able to load
+                * another segment.  This will allow us to immediately
+                * pickup on the next segment on the next data phase.
+                *
+                * If we happened to stop on the last segment, then
+                * our residual information is still correct from
+                * the idle loop and there is no need to perform
+                * any fixups.
+                */
+ultra2_ensure_sg:
+               test    SG_CACHE_SHADOW, LAST_SEG jz ultra2_shvalid;
+               /* Record if we've consumed all S/G entries */
+               test    SSTAT2, SHVALID jnz residuals_correct;
+               or      SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL;
+               jmp     residuals_correct;
+
+ultra2_shvalid:
+               test    SSTAT2, SHVALID jnz sgptr_fixup;
+               call    idle_loop;
+               jmp     ultra2_ensure_sg;
+
+sgptr_fixup:
+               /*
+                * Fixup the residual next S/G pointer.  The S/G preload
+                * feature of the chip allows us to load two elements
+                * in addition to the currently active element.  We
+                * store the bottom byte of the next S/G pointer in
+                * the SG_CACEPTR register so we can restore the
+                * correct value when the DMA completes.  If the next
+                * sg ptr value has advanced to the point where higher
+                * bytes in the address have been affected, fix them
+                * too.
+                */
+               test    SG_CACHE_SHADOW, 0x80 jz sgptr_fixup_done;
+               test    SCB_RESIDUAL_SGPTR[0], 0x80 jnz sgptr_fixup_done;
+               add     SCB_RESIDUAL_SGPTR[1], -1;
+               adc     SCB_RESIDUAL_SGPTR[2], -1; 
+               adc     SCB_RESIDUAL_SGPTR[3], -1;
+sgptr_fixup_done:
+               and     SCB_RESIDUAL_SGPTR[0], SG_ADDR_MASK, SG_CACHE_SHADOW;
+               clr     DATA_COUNT_ODD;
+               test    SG_CACHE_SHADOW, ODD_SEG jz . + 2;
+               or      DATA_COUNT_ODD, 0x1;
+               clr     SCB_RESIDUAL_DATACNT[3]; /* We are not the last seg */
+residuals_correct:
+               /*
+                * Go ahead and shut down the DMA engine now.
+                * In the future, we'll want to handle end of
+                * transfer messages prior to doing this, but this
+                * requires similar restructuring for pre-ULTRA2
+                * controllers.
+                */
+               test    DMAPARAMS, DIRECTION jnz ultra2_fifoempty;
+ultra2_fifoflush:
+               if ((ahc->features & AHC_DT) == 0) {
+                       if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) {
+                               /*
+                                * On Rev A of the aic7890, the autoflush
+                                * feature doesn't function correctly.
+                                * Perform an explicit manual flush.  During
+                                * a manual flush, the FIFOEMP bit becomes
+                                * true every time the PCI FIFO empties
+                                * regardless of the state of the SCSI FIFO.
+                                * It can take up to 4 clock cycles for the
+                                * SCSI FIFO to get data into the PCI FIFO
+                                * and for FIFOEMP to de-assert.  Here we
+                                * guard against this condition by making
+                                * sure the FIFOEMP bit stays on for 5 full
+                                * clock cycles.
+                                */
+                               or      DFCNTRL, FIFOFLUSH;
+                               test    DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+                               test    DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+                               test    DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+                               test    DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+                       }
+                       test    DFSTATUS, FIFOEMP jz ultra2_fifoflush;
+               } else {
+                       /*
+                        * We enable the auto-ack feature on DT capable
+                        * controllers.  This means that the controller may
+                        * have already transferred some overrun bytes into
+                        * the data FIFO and acked them on the bus.  The only
+                        * way to detect this situation is to wait for
+                        * LAST_SEG_DONE to come true on a completed transfer
+                        * and then test to see if the data FIFO is non-empty.
+                        */
+                       test    SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 4;
+                       test    SG_CACHE_SHADOW, LAST_SEG_DONE jz .;
+                       test    DFSTATUS, FIFOEMP jnz ultra2_fifoempty;
+                       /* Overrun */
+                       jmp     data_phase_loop;
+                       test    DFSTATUS, FIFOEMP jz .;
+               }
+ultra2_fifoempty:
+               /* Don't clobber an inprogress host data transfer */
+               test    DFSTATUS, MREQPEND      jnz ultra2_fifoempty;
+ultra2_dmahalt:
+               and     DFCNTRL, ~(SCSIEN|HDMAEN);
+               test    DFCNTRL, SCSIEN|HDMAEN jnz .;
+               if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+                       /*
+                        * Keep HHADDR cleared for future, 32bit addressed
+                        * only, DMA operations.
+                        *
+                        * Due to bayonette style S/G handling, our residual
+                        * data must be "fixed up" once the transfer is halted.
+                        * Here we fixup the HSHADDR stored in the high byte
+                        * of the residual data cnt.  By postponing the fixup,
+                        * we can batch the clearing of HADDR with the fixup.
+                        * If we halted on the last segment, the residual is
+                        * already correct.   If we are not on the last
+                        * segment, copy the high address directly from HSHADDR.
+                        * We don't need to worry about maintaining the
+                        * SG_LAST_SEG flag as it will always be false in the
+                        * case where an update is required.
+                        */
+                       or      DSCOMMAND1, HADDLDSEL0;
+                       test    SG_CACHE_SHADOW, LAST_SEG jnz . + 2;
+                       mov     SCB_RESIDUAL_DATACNT[3], SHADDR;
+                       clr     HADDR;
+                       and     DSCOMMAND1, ~HADDLDSEL0;
+               }
+       } else {
+               /* If we are the last SG block, tell the hardware. */
+               if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+                 && ahc->pci_cachesize != 0) {
+                       test    MWI_RESIDUAL, 0xFF jnz dma_mid_sg;
+               }
+               test    SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz dma_mid_sg;
+               if ((ahc->flags & AHC_TARGETROLE) != 0) {
+                       test    SSTAT0, TARGET jz dma_last_sg;
+                       if ((ahc->flags & AHC_TMODE_WIDEODD_BUG) != 0) {
+                               test    DMAPARAMS, DIRECTION jz dma_mid_sg;
+                       }
+               }
+dma_last_sg:
+               and     DMAPARAMS, ~WIDEODD;
+dma_mid_sg:
+               /* Start DMA data transfer. */
+               mov     DFCNTRL, DMAPARAMS;
+dma_loop:
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       call    idle_loop;
+               }
+               test    SSTAT0,DMADONE  jnz dma_dmadone;
+               test    SSTAT1,PHASEMIS jz dma_loop;    /* ie. underrun */
+dma_phasemis:
+               /*
+                * We will be "done" DMAing when the transfer count goes to
+                * zero, or the target changes the phase (in light of this,
+                * it makes sense that the DMA circuitry doesn't ACK when
+                * PHASEMIS is active).  If we are doing a SCSI->Host transfer,
+                * the data FIFO should be flushed auto-magically on STCNT=0
+                * or a phase change, so just wait for FIFO empty status.
+                */
+dma_checkfifo:
+               test    DFCNTRL,DIRECTION       jnz dma_fifoempty;
+dma_fifoflush:
+               test    DFSTATUS,FIFOEMP        jz dma_fifoflush;
+dma_fifoempty:
+               /* Don't clobber an inprogress host data transfer */
+               test    DFSTATUS, MREQPEND      jnz dma_fifoempty;
+
+               /*
+                * Now shut off the DMA and make sure that the DMA
+                * hardware has actually stopped.  Touching the DMA
+                * counters, etc. while a DMA is active will result
+                * in an ILLSADDR exception.
+                */
+dma_dmadone:
+               and     DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+dma_halt:
+               /*
+                * Some revisions of the aic78XX have a problem where, if the
+                * data fifo is full, but the PCI input latch is not empty, 
+                * HDMAEN cannot be cleared.  The fix used here is to drain
+                * the prefetched but unused data from the data fifo until
+                * there is space for the input latch to drain.
+                */
+               if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0) {
+                       mov     NONE, DFDAT;
+               }
+               test    DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
+
+               /* See if we have completed this last segment */
+               test    STCNT[0], 0xff  jnz data_phase_finish;
+               test    STCNT[1], 0xff  jnz data_phase_finish;
+               test    STCNT[2], 0xff  jnz data_phase_finish;
+
+               /*
+                * Advance the scatter-gather pointers if needed 
+                */
+               if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+                 && ahc->pci_cachesize != 0) {
+                       test    MWI_RESIDUAL, 0xFF jz no_mwi_resid;
+                       /*
+                        * Reload HADDR from SHADDR and setup the
+                        * count to be the size of our residual.
+                        */
+                       if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                               bmov    HADDR, SHADDR, 4;
+                               mov     HCNT, MWI_RESIDUAL;
+                               bmov    HCNT[1], ALLZEROS, 2;
+                       } else {
+                               mvi     DINDEX, HADDR;
+                               mvi     SHADDR call bcopy_4;
+                               mov     MWI_RESIDUAL call set_hcnt;
+                       }
+                       clr     MWI_RESIDUAL;
+                       jmp     sg_load_done;
+no_mwi_resid:
+               }
+               test    SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz sg_load;
+               or      SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL;
+               jmp     data_phase_finish;
+sg_load:
+               /*
+                * Load the next SG element's data address and length
+                * into the DMA engine.  If we don't have hardware
+                * to perform a prefetch, we'll have to fetch the
+                * segment from host memory first.
+                */
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       /* Wait for the idle loop to complete */
+                       test    CCSGCTL, CCSGEN jz . + 3;
+                       call    idle_loop;
+                       test    CCSGCTL, CCSGEN jnz . - 1;
+                       bmov    HADDR, CCSGRAM, 7;
+                       /*
+                        * Workaround for flaky external SCB RAM
+                        * on certain aic7895 setups.  It seems
+                        * unable to handle direct transfers from
+                        * S/G ram to certain SCB locations.
+                        */
+                       mov     SINDEX, CCSGRAM;
+                       mov     SCB_RESIDUAL_DATACNT[3], SINDEX;
+               } else {
+                       if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+                               mov     ALLZEROS call set_hhaddr;
+                       }
+                       mvi     DINDEX, HADDR;
+                       mvi     SCB_RESIDUAL_SGPTR      call bcopy_4;
+
+                       mvi     SG_SIZEOF       call set_hcnt;
+
+                       or      DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+
+                       call    dma_finish;
+
+                       mvi     DINDEX, HADDR;
+                       call    dfdat_in_7;
+                       mov     SCB_RESIDUAL_DATACNT[3], DFDAT;
+               }
+
+               if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+                       mov     SCB_RESIDUAL_DATACNT[3] call set_hhaddr;
+
+                       /*
+                        * The lowest address byte must be loaded
+                        * last as it triggers the computation of
+                        * some items in the PCI block.  The ULTRA2
+                        * chips do this on PRELOAD.
+                        */
+                       mov     HADDR, HADDR;
+               }
+               if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+                 && ahc->pci_cachesize != 0) {
+                       call calc_mwi_residual;
+               }
+
+               /* Point to the new next sg in memory */
+               call    sg_advance;
+
+sg_load_done:
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       bmov    STCNT, HCNT, 3;
+               } else {
+                       call    set_stcnt_from_hcnt;
+               }
+               /* Track odd'ness */
+               test    HCNT[0], 0x1 jz . + 2;
+               xor     DATA_COUNT_ODD, 0x1;
+
+               if ((ahc->flags & AHC_TARGETROLE) != 0) {
+                       test    SSTAT0, TARGET jnz data_phase_loop;
+               }
+       }
+data_phase_finish:
+       /*
+        * If the target has left us in data phase, loop through
+        * the dma code again.  In the case of ULTRA2 adapters,
+        * we should only loop if there is a data overrun.  For
+        * all other adapters, we'll loop after each S/G element
+        * is loaded as well as if there is an overrun.
+        */
+       if ((ahc->flags & AHC_TARGETROLE) != 0) {
+               test    SSTAT0, TARGET jnz data_phase_done;
+       }
+       if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+               test    SSTAT1, REQINIT jz .;
+               if ((ahc->features & AHC_DT) == 0) {
+                       test    SSTAT1,PHASEMIS jz data_phase_loop;
+               } else {
+                       test    SCSIPHASE, DATA_PHASE_MASK jnz data_phase_loop;
+               }
+       }
+
+data_phase_done:
+       /*
+        * After a DMA finishes, save the SG and STCNT residuals back into
+        * the SCB.  We use STCNT instead of HCNT, since it's a reflection
+        * of how many bytes were transferred on the SCSI (as opposed to the
+        * host) bus.
+        */
+       if ((ahc->features & AHC_CMD_CHAN) != 0) {
+               /* Kill off any pending prefetch */
+               call    disable_ccsgen;
+       }
+
+       if ((ahc->features & AHC_ULTRA2) == 0) {
+               /*
+                * Clear the high address byte so that all other DMA
+                * operations, which use 32bit addressing, can assume
+                * HHADDR is 0.
+                */
+               if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+                       mov     ALLZEROS call set_hhaddr;
+               }
+       }
+
+       /*
+        * Update our residual information before the information is
+        * lost by some other type of SCSI I/O (e.g. PIO).  If we have
+        * transferred all data, no update is needed.
+        *
+        */
+       test    SCB_RESIDUAL_SGPTR, SG_LIST_NULL jnz residual_update_done;
+       if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+         && ahc->pci_cachesize != 0) {
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       test    MWI_RESIDUAL, 0xFF jz bmov_resid;
+               }
+               mov     A, MWI_RESIDUAL;
+               add     SCB_RESIDUAL_DATACNT[0], A, STCNT[0];
+               clr     A;
+               adc     SCB_RESIDUAL_DATACNT[1], A, STCNT[1];
+               adc     SCB_RESIDUAL_DATACNT[2], A, STCNT[2];
+               clr     MWI_RESIDUAL;
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       jmp     . + 2;
+bmov_resid:
+                       bmov    SCB_RESIDUAL_DATACNT, STCNT, 3;
+               }
+       } else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+               bmov    SCB_RESIDUAL_DATACNT, STCNT, 3;
+       } else {
+               mov     SCB_RESIDUAL_DATACNT[0], STCNT[0];
+               mov     SCB_RESIDUAL_DATACNT[1], STCNT[1];
+               mov     SCB_RESIDUAL_DATACNT[2], STCNT[2];
+       }
+residual_update_done:
+       /*
+        * Since we've been through a data phase, the SCB_RESID* fields
+        * are now initialized.  Clear the full residual flag.
+        */
+       and     SCB_SGPTR[0], ~SG_FULL_RESID;
+
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               /* Clear the channel in case we return to data phase later */
+               or      SXFRCTL0, CLRSTCNT|CLRCHN;
+               or      SXFRCTL0, CLRSTCNT|CLRCHN;
+       }
+
+       if ((ahc->flags & AHC_TARGETROLE) != 0) {
+               test    SEQ_FLAGS, DPHASE_PENDING jz ITloop;
+               and     SEQ_FLAGS, ~DPHASE_PENDING;
+               /*
+                * For data-in phases, wait for any pending acks from the
+                * initiator before changing phase.  We only need to
+                * send Ignore Wide Residue messages for data-in phases.
+                */
+               test    DFCNTRL, DIRECTION jz target_ITloop;
+               test    SSTAT1, REQINIT jnz .;
+               test    DATA_COUNT_ODD, 0x1 jz target_ITloop;
+               test    SCSIRATE, WIDEXFER jz target_ITloop;
+               /*
+                * Issue an Ignore Wide Residue Message.
+                */
+               mvi     P_MESGIN|BSYO call change_phase;
+               mvi     MSG_IGN_WIDE_RESIDUE call target_outb;
+               mvi     1 call target_outb;
+               jmp     target_ITloop;
+       } else {
+               jmp     ITloop;
+       }
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+/*
+ * Command phase.  Set up the DMA registers and let 'er rip.
+ */
+p_command:
+       test    SEQ_FLAGS,IDENTIFY_SEEN jnz p_command_okay;
+       mvi     NO_IDENT jmp set_seqint;
+p_command_okay:
+
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               bmov    HCNT[0], SCB_CDB_LEN,  1;
+               bmov    HCNT[1], ALLZEROS, 2;
+               mvi     SG_CACHE_PRE, LAST_SEG;
+       } else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+               bmov    STCNT[0], SCB_CDB_LEN, 1;
+               bmov    STCNT[1], ALLZEROS, 2;
+       } else {
+               mov     STCNT[0], SCB_CDB_LEN;
+               clr     STCNT[1];
+               clr     STCNT[2];
+       }
+       add     NONE, -13, SCB_CDB_LEN;
+       mvi     SCB_CDB_STORE jnc p_command_embedded;
+p_command_from_host:
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               bmov    HADDR[0], SCB_CDB_PTR, 4;
+               mvi     DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
+       } else {
+               if ((ahc->features & AHC_CMD_CHAN) != 0) {
+                       bmov    HADDR[0], SCB_CDB_PTR, 4;
+                       bmov    HCNT, STCNT, 3;
+               } else {
+                       mvi     DINDEX, HADDR;
+                       mvi     SCB_CDB_PTR call bcopy_4;
+                       mov     SCB_CDB_LEN call set_hcnt;
+               }
+               mvi     DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET);
+       }
+       jmp     p_command_loop;
+p_command_embedded:
+       /*
+        * The data fifo seems to require 4 byte aligned
+        * transfers from the sequencer.  Force this to
+        * be the case by clearing HADDR[0] even though
+        * we aren't going to touch host memory.
+        */
+       clr     HADDR[0];
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               mvi     DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION);
+               bmov    DFDAT, SCB_CDB_STORE, 12; 
+       } else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+               if ((ahc->flags & AHC_SCB_BTT) != 0) {
+                       /*
+                        * On the 7895 the data FIFO will
+                        * get corrupted if you try to dump
+                        * data from external SCB memory into
+                        * the FIFO while it is enabled.  So,
+                        * fill the fifo and then enable SCSI
+                        * transfers.
+                        */
+                       mvi     DFCNTRL, (DIRECTION|FIFORESET);
+               } else {
+                       mvi     DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
+               }
+               bmov    DFDAT, SCB_CDB_STORE, 12; 
+               if ((ahc->flags & AHC_SCB_BTT) != 0) {
+                       mvi     DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFOFLUSH);
+               } else {
+                       or      DFCNTRL, FIFOFLUSH;
+               }
+       } else {
+               mvi     DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
+               call    copy_to_fifo_6;
+               call    copy_to_fifo_6;
+               or      DFCNTRL, FIFOFLUSH;
+       }
+p_command_loop:
+       if ((ahc->features & AHC_DT) == 0) {
+               test    SSTAT0, SDONE jnz . + 2;
+               test    SSTAT1, PHASEMIS jz p_command_loop;
+               /*
+                * Wait for our ACK to go-away on it's own
+                * instead of being killed by SCSIEN getting cleared.
+                */
+               test    SCSISIGI, ACKI jnz .;
+       } else {
+               test    DFCNTRL, SCSIEN jnz p_command_loop;
+       }
+       and     DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+       test    DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .;
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               /* Drop any residual from the S/G Preload queue */
+               or      SXFRCTL0, CLRSTCNT;
+       }
+       jmp     ITloop;
+
+/*
+ * Status phase.  Wait for the data byte to appear, then read it
+ * and store it into the SCB.
+ */
+p_status:
+       test    SEQ_FLAGS,IDENTIFY_SEEN jnz p_status_okay;
+       mvi     NO_IDENT jmp set_seqint;
+p_status_okay:
+       mov     SCB_SCSI_STATUS, SCSIDATL;
+       jmp     ITloop;
+
+/*
+ * Message out phase.  If MSG_OUT is MSG_IDENTIFYFLAG, build a full
+ * indentify message sequence and send it to the target.  The host may
+ * override this behavior by setting the MK_MESSAGE bit in the SCB
+ * control byte.  This will cause us to interrupt the host and allow
+ * it to handle the message phase completely on its own.  If the bit
+ * associated with this target is set, we will also interrupt the host,
+ * thereby allowing it to send a message on the next selection regardless
+ * of the transaction being sent.
+ * 
+ * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
+ * This is done to allow the host to send messages outside of an identify
+ * sequence while protecting the seqencer from testing the MK_MESSAGE bit
+ * on an SCB that might not be for the current nexus. (For example, a
+ * BDR message in responce to a bad reselection would leave us pointed to
+ * an SCB that doesn't have anything to do with the current target).
+ *
+ * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
+ * bus device reset).
+ *
+ * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
+ * in case the target decides to put us in this phase for some strange
+ * reason.
+ */
+p_mesgout_retry:
+       /* Turn on ATN for the retry */
+       if ((ahc->features & AHC_DT) == 0) {
+               or      SCSISIGO, ATNO, LASTPHASE;
+       } else {
+               mvi     SCSISIGO, ATNO;
+       }
+p_mesgout:
+       mov     SINDEX, MSG_OUT;
+       cmp     SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
+       test    SCB_CONTROL,MK_MESSAGE  jnz host_message_loop;
+p_mesgout_identify:
+       or      SINDEX, MSG_IDENTIFYFLAG|DISCENB, SCB_LUN;
+       test    SCB_CONTROL, DISCENB jnz . + 2;
+       and     SINDEX, ~DISCENB;
+/*
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
+ */
+p_mesgout_tag:
+       test    SCB_CONTROL,TAG_ENB jz  p_mesgout_onebyte;
+       mov     SCSIDATL, SINDEX;       /* Send the identify message */
+       call    phase_lock;
+       cmp     LASTPHASE, P_MESGOUT    jne p_mesgout_done;
+       and     SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+       call    phase_lock;
+       cmp     LASTPHASE, P_MESGOUT    jne p_mesgout_done;
+       mov     SCB_TAG jmp p_mesgout_onebyte;
+/*
+ * Interrupt the driver, and allow it to handle this message
+ * phase and any required retries.
+ */
+p_mesgout_from_host:
+       cmp     SINDEX, HOST_MSG        jne p_mesgout_onebyte;
+       jmp     host_message_loop;
+
+p_mesgout_onebyte:
+       mvi     CLRSINT1, CLRATNO;
+       mov     SCSIDATL, SINDEX;
+
+/*
+ * If the next bus phase after ATN drops is message out, it means
+ * that the target is requesting that the last message(s) be resent.
+ */
+       call    phase_lock;
+       cmp     LASTPHASE, P_MESGOUT    je p_mesgout_retry;
+
+p_mesgout_done:
+       mvi     CLRSINT1,CLRATNO;       /* Be sure to turn ATNO off */
+       mov     LAST_MSG, MSG_OUT;
+       mvi     MSG_OUT, MSG_NOOP;      /* No message left */
+       jmp     ITloop;
+
+/*
+ * Message in phase.  Bytes are read using Automatic PIO mode.
+ */
+p_mesgin:
+       mvi     ACCUM           call inb_first; /* read the 1st message byte */
+
+       test    A,MSG_IDENTIFYFLAG      jnz mesgin_identify;
+       cmp     A,MSG_DISCONNECT        je mesgin_disconnect;
+       cmp     A,MSG_SAVEDATAPOINTER   je mesgin_sdptrs;
+       cmp     ALLZEROS,A              je mesgin_complete;
+       cmp     A,MSG_RESTOREPOINTERS   je mesgin_rdptrs;
+       cmp     A,MSG_IGN_WIDE_RESIDUE  je mesgin_ign_wide_residue;
+       cmp     A,MSG_NOOP              je mesgin_done;
+
+/*
+ * Pushed message loop to allow the kernel to
+ * run it's own message state engine.  To avoid an
+ * extra nop instruction after signaling the kernel,
+ * we perform the phase_lock before checking to see
+ * if we should exit the loop and skip the phase_lock
+ * in the ITloop.  Performing back to back phase_locks
+ * shouldn't hurt, but why do it twice...
+ */
+host_message_loop:
+       mvi     HOST_MSG_LOOP call set_seqint;
+       call    phase_lock;
+       cmp     RETURN_1, EXIT_MSG_LOOP je ITloop + 1;
+       jmp     host_message_loop;
+
+mesgin_ign_wide_residue:
+if ((ahc->features & AHC_WIDE) != 0) {
+       test    SCSIRATE, WIDEXFER jz mesgin_reject;
+       /* Pull the residue byte */
+       mvi     ARG_1   call inb_next;
+       cmp     ARG_1, 0x01 jne mesgin_reject;
+       test    SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 2;
+       test    DATA_COUNT_ODD, 0x1     jz mesgin_done;
+       mvi     IGN_WIDE_RES call set_seqint;
+       jmp     mesgin_done;
+}
+
+mesgin_reject:
+       mvi     MSG_MESSAGE_REJECT      call mk_mesg;
+mesgin_done:
+       mov     NONE,SCSIDATL;          /*dummy read from latch to ACK*/
+       jmp     ITloop;
+
+mesgin_complete:
+/*
+ * We received a "command complete" message.  Put the SCB_TAG into the QOUTFIFO,
+ * and trigger a completion interrupt.  Before doing so, check to see if there
+ * is a residual or the status byte is something other than STATUS_GOOD (0).
+ * In either of these conditions, we upload the SCB back to the host so it can
+ * process this information.  In the case of a non zero status byte, we 
+ * additionally interrupt the kernel driver synchronously, allowing it to
+ * decide if sense should be retrieved.  If the kernel driver wishes to request
+ * sense, it will fill the kernel SCB with a request sense command, requeue
+ * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting 
+ * RETURN_1 to SEND_SENSE.
+ */
+
+/*
+ * If ATN is raised, we still want to give the target a message.
+ * Perhaps there was a parity error on this last message byte.
+ * Either way, the target should take us to message out phase
+ * and then attempt to complete the command again.  We should use a
+ * critical section here to guard against a timeout triggering
+ * for this command and setting ATN while we are still processing
+ * the completion.
+       test    SCSISIGI, ATNI jnz mesgin_done;
+ */
+
+/*
+ * See if we attempted to deliver a message but the target ingnored us.
+ */
+       test    SCB_CONTROL, MK_MESSAGE jz . + 2;
+       mvi     MKMSG_FAILED call set_seqint;
+
+/*
+ * Check for residuals
+ */
+       test    SCB_SGPTR, SG_LIST_NULL jnz check_status;/* No xfer */
+       test    SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
+       test    SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;
+check_status:
+       test    SCB_SCSI_STATUS,0xff    jz complete;    /* Good Status? */
+upload_scb:
+       or      SCB_SGPTR, SG_RESID_VALID;
+       mvi     DMAPARAMS, FIFORESET;
+       mov     SCB_TAG         call dma_scb;
+       test    SCB_SCSI_STATUS, 0xff   jz complete;    /* Just a residual? */
+       mvi     BAD_STATUS call set_seqint;             /* let driver know */
+       cmp     RETURN_1, SEND_SENSE    jne complete;
+       call    add_scb_to_free_list;
+       jmp     await_busfree;
+complete:
+       mov     SCB_TAG call complete_post;
+       jmp     await_busfree;
+}
+
+complete_post:
+       /* Post the SCBID in SINDEX and issue an interrupt */
+       call    add_scb_to_free_list;
+       mov     ARG_1, SINDEX;
+       if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+               mov     A, SDSCB_QOFF;
+       } else {
+               mov     A, QOUTPOS;
+       }
+       mvi     QOUTFIFO_OFFSET call post_byte_setup;
+       mov     ARG_1 call post_byte;
+       if ((ahc->features & AHC_QUEUE_REGS) == 0) {
+               inc     QOUTPOS;
+       }
+       mvi     INTSTAT,CMDCMPLT ret;
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+/*
+ * Is it a disconnect message?  Set a flag in the SCB to remind us
+ * and await the bus going free.  If this is an untagged transaction
+ * store the SCB id for it in our untagged target table for lookup on
+ * a reselction.
+ */
+mesgin_disconnect:
+       /*
+        * If ATN is raised, we still want to give the target a message.
+        * Perhaps there was a parity error on this last message byte
+        * or we want to abort this command.  Either way, the target
+        * should take us to message out phase and then attempt to
+        * disconnect again.
+        * XXX - Wait for more testing.
+       test    SCSISIGI, ATNI jnz mesgin_done;
+        */
+
+       or      SCB_CONTROL,DISCONNECTED;
+       if ((ahc->flags & AHC_PAGESCBS) != 0) {
+               call    add_scb_to_disc_list;
+       }
+       test    SCB_CONTROL, TAG_ENB jnz await_busfree;
+       mov     ARG_1, SCB_TAG;
+       mov     SAVED_LUN, SCB_LUN;
+       mov     SCB_SCSIID      call set_busy_target;
+       jmp     await_busfree;
+
+/*
+ * Save data pointers message:
+ * Copying RAM values back to SCB, for Save Data Pointers message, but
+ * only if we've actually been into a data phase to change them.  This
+ * protects against bogus data in scratch ram and the residual counts
+ * since they are only initialized when we go into data_in or data_out.
+ * Ack the message as soon as possible.  For chips without S/G pipelining,
+ * we can only ack the message after SHADDR has been saved.  On these
+ * chips, SHADDR increments with every bus transaction, even PIO.
+ */
+mesgin_sdptrs:
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               mov     NONE,SCSIDATL;          /*dummy read from latch to ACK*/
+               test    SEQ_FLAGS, DPHASE       jz ITloop;
+       } else {
+               test    SEQ_FLAGS, DPHASE       jz mesgin_done;
+       }
+
+       /*
+        * If we are asked to save our position at the end of the
+        * transfer, just mark us at the end rather than perform a
+        * full save.
+        */
+       test    SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz mesgin_sdptrs_full;
+       or      SCB_SGPTR, SG_LIST_NULL;
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               jmp     ITloop;
+       } else {
+               jmp     mesgin_done;
+       }
+
+mesgin_sdptrs_full:
+
+       /*
+        * The SCB_SGPTR becomes the next one we'll download,
+        * and the SCB_DATAPTR becomes the current SHADDR.
+        * Use the residual number since STCNT is corrupted by
+        * any message transfer.
+        */
+       if ((ahc->features & AHC_CMD_CHAN) != 0) {
+               bmov    SCB_DATAPTR, SHADDR, 4;
+               if ((ahc->features & AHC_ULTRA2) == 0) {
+                       mov     NONE,SCSIDATL;  /*dummy read from latch to ACK*/
+               }
+               bmov    SCB_DATACNT, SCB_RESIDUAL_DATACNT, 8;
+       } else {
+               mvi     DINDEX, SCB_DATAPTR;
+               mvi     SHADDR call bcopy_4;
+               mov     NONE,SCSIDATL;  /*dummy read from latch to ACK*/
+               mvi     SCB_RESIDUAL_DATACNT call bcopy_8;
+       }
+       jmp     ITloop;
+
+/*
+ * Restore pointers message?  Data pointers are recopied from the
+ * SCB anytime we enter a data phase for the first time, so all
+ * we need to do is clear the DPHASE flag and let the data phase
+ * code do the rest.
+ */
+mesgin_rdptrs:
+       and     SEQ_FLAGS, ~DPHASE;             /*
+                                                * We'll reload them
+                                                * the next time through
+                                                * the dataphase.
+                                                */
+       jmp     mesgin_done;
+
+/*
+ * Index into our Busy Target table.  SINDEX and DINDEX are modified
+ * upon return.  SCBPTR may be modified by this action.
+ */
+set_busy_target:
+       shr     DINDEX, 4, SINDEX;
+       if ((ahc->flags & AHC_SCB_BTT) != 0) {
+               mov     SCBPTR, SAVED_LUN;
+               add     DINDEX, SCB_64_BTT;
+       } else {
+               add     DINDEX, BUSY_TARGETS;
+       }
+       mov     DINDIR, ARG_1 ret;
+
+/*
+ * Identify message?  For a reconnecting target, this tells us the lun
+ * that the reconnection is for - find the correct SCB and switch to it,
+ * clearing the "disconnected" bit so we don't "find" it by accident later.
+ */
+mesgin_identify:
+       /*
+        * Determine whether a target is using tagged or non-tagged
+        * transactions by first looking at the transaction stored in
+        * the busy target array.  If there is no untagged transaction
+        * for this target or the transaction is for a different lun, then
+        * this must be an untagged transaction.
+        */
+       shr     SINDEX, 4, SAVED_SCSIID;
+       and     SAVED_LUN, MSG_IDENTIFY_LUNMASK, A;
+       if ((ahc->flags & AHC_SCB_BTT) != 0) {
+               add     SINDEX, SCB_64_BTT;
+               mov     SCBPTR, SAVED_LUN;
+               if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+                       add     NONE, -SCB_64_BTT, SINDEX;
+                       jc      . + 2;
+                       mvi     INTSTAT, OUT_OF_RANGE;
+                       nop;
+                       add     NONE, -(SCB_64_BTT + 16), SINDEX;
+                       jnc     . + 2;
+                       mvi     INTSTAT, OUT_OF_RANGE;
+                       nop;
+               }
+       } else {
+               add     SINDEX, BUSY_TARGETS;
+               if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+                       add     NONE, -BUSY_TARGETS, SINDEX;
+                       jc      . + 2;
+                       mvi     INTSTAT, OUT_OF_RANGE;
+                       nop;
+                       add     NONE, -(BUSY_TARGETS + 16), SINDEX;
+                       jnc     . + 2;
+                       mvi     INTSTAT, OUT_OF_RANGE;
+                       nop;
+               }
+       }
+       mov     ARG_1, SINDIR;
+       cmp     ARG_1, SCB_LIST_NULL    je snoop_tag;
+       if ((ahc->flags & AHC_PAGESCBS) != 0) {
+               mov     ARG_1 call findSCB;
+       } else {
+               mov     SCBPTR, ARG_1;
+       }
+       if ((ahc->flags & AHC_SCB_BTT) != 0) {
+               jmp setup_SCB_id_lun_okay;
+       } else {
+               /*
+                * We only allow one untagged command per-target
+                * at a time.  So, if the lun doesn't match, look
+                * for a tag message.
+                */
+               mov     A, SCB_LUN;
+               cmp     SAVED_LUN, A    je setup_SCB_id_lun_okay;
+               if ((ahc->flags & AHC_PAGESCBS) != 0) {
+                       /*
+                        * findSCB removes the SCB from the
+                        * disconnected list, so we must replace
+                        * it there should this SCB be for another
+                        * lun.
+                        */
+                       call    cleanup_scb;
+               }
+       }
+
+/*
+ * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
+ * If we get one, we use the tag returned to find the proper
+ * SCB.  With SCB paging, we must search for non-tagged
+ * transactions since the SCB may exist in any slot.  If we're not
+ * using SCB paging, we can use the tag as the direct index to the
+ * SCB.
+ */
+snoop_tag:
+       if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+               or      SEQ_FLAGS, 0x80;
+       }
+       mov     NONE,SCSIDATL;          /* ACK Identify MSG */
+       call    phase_lock;
+       if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+               or      SEQ_FLAGS, 0x1;
+       }
+       cmp     LASTPHASE, P_MESGIN     jne not_found;
+       if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+               or      SEQ_FLAGS, 0x2;
+       }
+       cmp     SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
+get_tag:
+       if ((ahc->flags & AHC_PAGESCBS) != 0) {
+               mvi     ARG_1   call inb_next;  /* tag value */
+               mov     ARG_1   call findSCB;
+       } else {
+               mvi     ARG_1   call inb_next;  /* tag value */
+               mov     SCBPTR, ARG_1;
+       }
+
+/*
+ * Ensure that the SCB the tag points to is for
+ * an SCB transaction to the reconnecting target.
+ */
+setup_SCB:
+       if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+               or      SEQ_FLAGS, 0x4;
+       }
+       mov     A, SCB_SCSIID;
+       cmp     SAVED_SCSIID, A jne not_found_cleanup_scb;
+       if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+               or      SEQ_FLAGS, 0x8;
+       }
+setup_SCB_id_okay:
+       mov     A, SCB_LUN;
+       cmp     SAVED_LUN, A    jne not_found_cleanup_scb;
+setup_SCB_id_lun_okay:
+       if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+               or      SEQ_FLAGS, 0x10;
+       }
+       test    SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
+       and     SCB_CONTROL,~DISCONNECTED;
+       test    SCB_CONTROL, TAG_ENB    jnz setup_SCB_tagged;
+       if ((ahc->flags & AHC_SCB_BTT) != 0) {
+               mov     A, SCBPTR;
+       }
+       mvi     ARG_1, SCB_LIST_NULL;
+       mov     SAVED_SCSIID    call    set_busy_target;
+       if ((ahc->flags & AHC_SCB_BTT) != 0) {
+               mov     SCBPTR, A;
+       }
+setup_SCB_tagged:
+       mvi     SEQ_FLAGS,IDENTIFY_SEEN;        /* make note of IDENTIFY */
+       call    set_transfer_settings;
+       /* See if the host wants to send a message upon reconnection */
+       test    SCB_CONTROL, MK_MESSAGE jz mesgin_done;
+       mvi     HOST_MSG        call mk_mesg;
+       jmp     mesgin_done;
+
+not_found_cleanup_scb:
+       if ((ahc->flags & AHC_PAGESCBS) != 0) {
+               call    cleanup_scb;
+       }
+not_found:
+       mvi     NO_MATCH call set_seqint;
+       jmp     mesgin_done;
+
+mk_mesg:
+       if ((ahc->features & AHC_DT) == 0) {
+               or      SCSISIGO, ATNO, LASTPHASE;
+       } else {
+               mvi     SCSISIGO, ATNO;
+       }
+       mov     MSG_OUT,SINDEX ret;
+
+/*
+ * Functions to read data in Automatic PIO mode.
+ *
+ * According to Adaptec's documentation, an ACK is not sent on input from
+ * the target until SCSIDATL is read from.  So we wait until SCSIDATL is
+ * latched (the usual way), then read the data byte directly off the bus
+ * using SCSIBUSL.  When we have pulled the ATN line, or we just want to
+ * acknowledge the byte, then we do a dummy read from SCISDATL.  The SCSI
+ * spec guarantees that the target will hold the data byte on the bus until
+ * we send our ACK.
+ *
+ * The assumption here is that these are called in a particular sequence,
+ * and that REQ is already set when inb_first is called.  inb_{first,next}
+ * use the same calling convention as inb.
+ */
+inb_next_wait_perr:
+       mvi     PERR_DETECTED call set_seqint;
+       jmp     inb_next_wait;
+inb_next:
+       mov     NONE,SCSIDATL;          /*dummy read from latch to ACK*/
+inb_next_wait:
+       /*
+        * If there is a parity error, wait for the kernel to
+        * see the interrupt and prepare our message response
+        * before continuing.
+        */
+       test    SSTAT1, REQINIT jz inb_next_wait;
+       test    SSTAT1, SCSIPERR jnz inb_next_wait_perr;
+inb_next_check_phase:
+       and     LASTPHASE, PHASE_MASK, SCSISIGI;
+       cmp     LASTPHASE, P_MESGIN jne mesgin_phasemis;
+inb_first:
+       mov     DINDEX,SINDEX;
+       mov     DINDIR,SCSIBUSL ret;            /*read byte directly from bus*/
+inb_last:
+       mov     NONE,SCSIDATL ret;              /*dummy read from latch to ACK*/
+}
+
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
+/*
+ * Change to a new phase.  If we are changing the state of the I/O signal,
+ * from out to in, wait an additional data release delay before continuing.
+ */
+change_phase:
+       /* Wait for preceeding I/O session to complete. */
+       test    SCSISIGI, ACKI jnz .;
+
+       /* Change the phase */
+       and     DINDEX, IOI, SCSISIGI;
+       mov     SCSISIGO, SINDEX;
+       and     A, IOI, SINDEX;
+
+       /*
+        * If the data direction has changed, from
+        * out (initiator driving) to in (target driving),
+        * we must wait at least a data release delay plus
+        * the normal bus settle delay. [SCSI III SPI 10.11.0]
+        */
+       cmp     DINDEX, A je change_phase_wait;
+       test    SINDEX, IOI jz change_phase_wait;
+       call    change_phase_wait;
+change_phase_wait:
+       nop;
+       nop;
+       nop;
+       nop ret;
+
+/*
+ * Send a byte to an initiator in Automatic PIO mode.
+ */
+target_outb:
+       or      SXFRCTL0, SPIOEN;
+       test    SSTAT0, SPIORDY jz .;
+       mov     SCSIDATL, SINDEX;
+       test    SSTAT0, SPIORDY jz .;
+       and     SXFRCTL0, ~SPIOEN ret;
+}
+       
+/*
+ * Locate a disconnected SCB by SCBID.  Upon return, SCBPTR and SINDEX will
+ * be set to the position of the SCB.  If the SCB cannot be found locally,
+ * it will be paged in from host memory.  RETURN_2 stores the address of the
+ * preceding SCB in the disconnected list which can be used to speed up
+ * removal of the found SCB from the disconnected list.
+ */
+if ((ahc->flags & AHC_PAGESCBS) != 0) {
+BEGIN_CRITICAL;
+findSCB:
+       mov     A, SINDEX;                      /* Tag passed in SINDEX */
+       cmp     DISCONNECTED_SCBH, SCB_LIST_NULL je findSCB_notFound;
+       mov     SCBPTR, DISCONNECTED_SCBH;      /* Initialize SCBPTR */
+       mvi     ARG_2, SCB_LIST_NULL;           /* Head of list */
+       jmp     findSCB_loop;
+findSCB_next:
+       cmp     SCB_NEXT, SCB_LIST_NULL je findSCB_notFound;
+       mov     ARG_2, SCBPTR;
+       mov     SCBPTR,SCB_NEXT;
+findSCB_loop:
+       cmp     SCB_TAG, A      jne findSCB_next;
+rem_scb_from_disc_list:
+       cmp     ARG_2, SCB_LIST_NULL    je rHead;
+       mov     DINDEX, SCB_NEXT;
+       mov     SINDEX, SCBPTR;
+       mov     SCBPTR, ARG_2;
+       mov     SCB_NEXT, DINDEX;
+       mov     SCBPTR, SINDEX ret;
+rHead:
+       mov     DISCONNECTED_SCBH,SCB_NEXT ret;
+END_CRITICAL;
+findSCB_notFound:
+       /*
+        * We didn't find it.  Page in the SCB.
+        */
+       mov     ARG_1, A; /* Save tag */
+       mov     ALLZEROS call get_free_or_disc_scb;
+       mvi     DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+       mov     ARG_1   jmp dma_scb;
+}
+
+/*
+ * Prepare the hardware to post a byte to host memory given an
+ * index of (A + (256 * SINDEX)) and a base address of SHARED_DATA_ADDR.
+ */
+post_byte_setup:
+       mov     ARG_2, SINDEX;
+       if ((ahc->features & AHC_CMD_CHAN) != 0) {
+               mvi     DINDEX, CCHADDR;
+               mvi     SHARED_DATA_ADDR call   set_1byte_addr;
+               mvi     CCHCNT, 1;
+               mvi     CCSCBCTL, CCSCBRESET ret;
+       } else {
+               mvi     DINDEX, HADDR;
+               mvi     SHARED_DATA_ADDR call   set_1byte_addr;
+               mvi     1       call set_hcnt;
+               mvi     DFCNTRL, FIFORESET ret;
+       }
+
+post_byte:
+       if ((ahc->features & AHC_CMD_CHAN) != 0) {
+               bmov    CCSCBRAM, SINDEX, 1;
+               or      CCSCBCTL, CCSCBEN|CCSCBRESET;
+               test    CCSCBCTL, CCSCBDONE jz .;
+               clr     CCSCBCTL ret;
+       } else {
+               mov     DFDAT, SINDEX;
+               or      DFCNTRL, HDMAEN|FIFOFLUSH;
+               jmp     dma_finish;
+       }
+
+phase_lock_perr:
+       mvi     PERR_DETECTED call set_seqint;
+phase_lock:     
+       /*
+        * If there is a parity error, wait for the kernel to
+        * see the interrupt and prepare our message response
+        * before continuing.
+        */
+       test    SSTAT1, REQINIT jz phase_lock;
+       test    SSTAT1, SCSIPERR jnz phase_lock_perr;
+phase_lock_latch_phase:
+       if ((ahc->features & AHC_DT) == 0) {
+               and     SCSISIGO, PHASE_MASK, SCSISIGI;
+       }
+       and     LASTPHASE, PHASE_MASK, SCSISIGI ret;
+
+if ((ahc->features & AHC_CMD_CHAN) == 0) {
+set_hcnt:
+       mov     HCNT[0], SINDEX;
+clear_hcnt:
+       clr     HCNT[1];
+       clr     HCNT[2] ret;
+
+set_stcnt_from_hcnt:
+       mov     STCNT[0], HCNT[0];
+       mov     STCNT[1], HCNT[1];
+       mov     STCNT[2], HCNT[2] ret;
+
+bcopy_8:
+       mov     DINDIR, SINDIR;
+bcopy_7:
+       mov     DINDIR, SINDIR;
+       mov     DINDIR, SINDIR;
+bcopy_5:
+       mov     DINDIR, SINDIR;
+bcopy_4:
+       mov     DINDIR, SINDIR;
+bcopy_3:
+       mov     DINDIR, SINDIR;
+       mov     DINDIR, SINDIR;
+       mov     DINDIR, SINDIR ret;
+}
+
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
+/*
+ * Setup addr assuming that A is an index into
+ * an array of 32byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
+ */
+set_32byte_addr:
+       shr     ARG_2, 3, A;
+       shl     A, 5;
+       jmp     set_1byte_addr;
+}
+
+/*
+ * Setup addr assuming that A is an index into
+ * an array of 64byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
+ */
+set_64byte_addr:
+       shr     ARG_2, 2, A;
+       shl     A, 6;
+
+/*
+ * Setup addr assuming that A + (ARG_2 * 256) is an
+ * index into an array of 1byte objects, SINDEX contains
+ * the base address of that array, and DINDEX contains
+ * the base address of the location to store the computed
+ * address.
+ */
+set_1byte_addr:
+       add     DINDIR, A, SINDIR;
+       mov     A, ARG_2;
+       adc     DINDIR, A, SINDIR;
+       clr     A;
+       adc     DINDIR, A, SINDIR;
+       adc     DINDIR, A, SINDIR ret;
+
+/*
+ * Either post or fetch an SCB from host memory based on the
+ * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
+ */
+dma_scb:
+       mov     A, SINDEX;
+       if ((ahc->features & AHC_CMD_CHAN) != 0) {
+               mvi     DINDEX, CCHADDR;
+               mvi     HSCB_ADDR call set_64byte_addr;
+               mov     CCSCBPTR, SCBPTR;
+               test    DMAPARAMS, DIRECTION jz dma_scb_tohost;
+               if ((ahc->flags & AHC_SCB_BTT) != 0) {
+                       mvi     CCHCNT, SCB_DOWNLOAD_SIZE_64;
+               } else {
+                       mvi     CCHCNT, SCB_DOWNLOAD_SIZE;
+               }
+               mvi     CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
+               cmp     CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
+               jmp     dma_scb_finish;
+dma_scb_tohost:
+               mvi     CCHCNT, SCB_UPLOAD_SIZE;
+               if ((ahc->features & AHC_ULTRA2) == 0) {
+                       mvi     CCSCBCTL, CCSCBRESET;
+                       bmov    CCSCBRAM, SCB_BASE, SCB_UPLOAD_SIZE;
+                       or      CCSCBCTL, CCSCBEN|CCSCBRESET;
+                       test    CCSCBCTL, CCSCBDONE jz .;
+               } else if ((ahc->bugs & AHC_SCBCHAN_UPLOAD_BUG) != 0) {
+                       mvi     CCSCBCTL, CCARREN|CCSCBRESET;
+                       cmp     CCSCBCTL, ARRDONE|CCARREN jne .;
+                       mvi     CCHCNT, SCB_UPLOAD_SIZE;
+                       mvi     CCSCBCTL, CCSCBEN|CCSCBRESET;
+                       cmp     CCSCBCTL, CCSCBDONE|CCSCBEN jne .;
+               } else {
+                       mvi     CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
+                       cmp     CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
+               }
+dma_scb_finish:
+               clr     CCSCBCTL;
+               test    CCSCBCTL, CCARREN|CCSCBEN jnz .;
+               ret;
+       } else {
+               mvi     DINDEX, HADDR;
+               mvi     HSCB_ADDR call set_64byte_addr;
+               mvi     SCB_DOWNLOAD_SIZE call set_hcnt;
+               mov     DFCNTRL, DMAPARAMS;
+               test    DMAPARAMS, DIRECTION    jnz dma_scb_fromhost;
+               /* Fill it with the SCB data */
+copy_scb_tofifo:
+               mvi     SINDEX, SCB_BASE;
+               add     A, SCB_DOWNLOAD_SIZE, SINDEX;
+copy_scb_tofifo_loop:
+               call    copy_to_fifo_8;
+               cmp     SINDEX, A jne copy_scb_tofifo_loop;
+               or      DFCNTRL, HDMAEN|FIFOFLUSH;
+               jmp     dma_finish;
+dma_scb_fromhost:
+               mvi     DINDEX, SCB_BASE;
+               if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0) {
+                       /*
+                        * The PCI module will only issue a PCI
+                        * retry if the data FIFO is empty.  If the
+                        * host disconnects in the middle of a
+                        * transfer, we must empty the fifo of all
+                        * available data to force the chip to
+                        * continue the transfer.  This does not
+                        * happen for SCSI transfers as the SCSI module
+                        * will drain the FIFO as data is made available.
+                        * When the hang occurs, we know that a multiple
+                        * of 8 bytes are in the FIFO because the PCI
+                        * module has an 8 byte input latch that only
+                        * dumps to the FIFO when HCNT == 0 or the
+                        * latch is full.
+                        */
+                       clr     A;
+                       /* Wait for at least 8 bytes of data to arrive. */
+dma_scb_hang_fifo:
+                       test    DFSTATUS, FIFOQWDEMP jnz dma_scb_hang_fifo;
+dma_scb_hang_wait:
+                       test    DFSTATUS, MREQPEND jnz dma_scb_hang_wait;
+                       test    DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
+                       test    DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
+                       test    DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
+                       /*
+                        * The PCI module no longer intends to perform
+                        * a PCI transaction.  Drain the fifo.
+                        */
+dma_scb_hang_dma_drain_fifo:
+                       not     A, HCNT;
+                       add     A, SCB_DOWNLOAD_SIZE+SCB_BASE+1;
+                       and     A, ~0x7;
+                       mov     DINDIR,DFDAT;
+                       cmp     DINDEX, A jne . - 1;
+                       cmp     DINDEX, SCB_DOWNLOAD_SIZE+SCB_BASE
+                               je      dma_finish_nowait;
+                       /* Restore A as the lines left to transfer. */
+                       add     A, -SCB_BASE, DINDEX;
+                       shr     A, 3;
+                       jmp     dma_scb_hang_fifo;
+dma_scb_hang_dma_done:
+                       and     DFCNTRL, ~HDMAEN;
+                       test    DFCNTRL, HDMAEN jnz .;
+                       add     SEQADDR0, A;
+               } else {
+                       call    dma_finish;
+               }
+               /* If we were putting the SCB, we are done */
+               call    dfdat_in_8;
+               call    dfdat_in_8;
+               call    dfdat_in_8;
+dfdat_in_8:
+               mov     DINDIR,DFDAT;
+dfdat_in_7:
+               mov     DINDIR,DFDAT;
+               mov     DINDIR,DFDAT;
+               mov     DINDIR,DFDAT;
+               mov     DINDIR,DFDAT;
+               mov     DINDIR,DFDAT;
+dfdat_in_2:
+               mov     DINDIR,DFDAT;
+               mov     DINDIR,DFDAT ret;
+       }
+
+copy_to_fifo_8:
+       mov     DFDAT,SINDIR;
+       mov     DFDAT,SINDIR;
+copy_to_fifo_6:
+       mov     DFDAT,SINDIR;
+copy_to_fifo_5:
+       mov     DFDAT,SINDIR;
+copy_to_fifo_4:
+       mov     DFDAT,SINDIR;
+       mov     DFDAT,SINDIR;
+       mov     DFDAT,SINDIR;
+       mov     DFDAT,SINDIR ret;
+
+/*
+ * Wait for DMA from host memory to data FIFO to complete, then disable
+ * DMA and wait for it to acknowledge that it's off.
+ */
+dma_finish:
+       test    DFSTATUS,HDONE  jz dma_finish;
+dma_finish_nowait:
+       /* Turn off DMA */
+       and     DFCNTRL, ~HDMAEN;
+       test    DFCNTRL, HDMAEN jnz .;
+       ret;
+
+/*
+ * Restore an SCB that failed to match an incoming reselection
+ * to the correct/safe state.  If the SCB is for a disconnected
+ * transaction, it must be returned to the disconnected list.
+ * If it is not in the disconnected state, it must be free.
+ */
+cleanup_scb:
+       if ((ahc->flags & AHC_PAGESCBS) != 0) {
+               test    SCB_CONTROL,DISCONNECTED jnz add_scb_to_disc_list;
+       }
+add_scb_to_free_list:
+       if ((ahc->flags & AHC_PAGESCBS) != 0) {
+BEGIN_CRITICAL;
+               mov     SCB_NEXT, FREE_SCBH;
+               mvi     SCB_TAG, SCB_LIST_NULL;
+               mov     FREE_SCBH, SCBPTR ret;
+END_CRITICAL;
+       } else {
+               mvi     SCB_TAG, SCB_LIST_NULL ret;
+       }
+
+if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+set_hhaddr:
+       or      DSCOMMAND1, HADDLDSEL0;
+       and     HADDR, SG_HIGH_ADDR_BITS, SINDEX;
+       and     DSCOMMAND1, ~HADDLDSEL0 ret;
+}
+
+if ((ahc->flags & AHC_PAGESCBS) != 0) {
+get_free_or_disc_scb:
+BEGIN_CRITICAL;
+       cmp     FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
+       cmp     DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
+return_error:
+       mvi     NO_FREE_SCB call set_seqint;
+       mvi     SINDEX, SCB_LIST_NULL   ret;
+dequeue_disc_scb:
+       mov     SCBPTR, DISCONNECTED_SCBH;
+       mov     DISCONNECTED_SCBH, SCB_NEXT;
+END_CRITICAL;
+       mvi     DMAPARAMS, FIFORESET;
+       mov     SCB_TAG jmp dma_scb;
+BEGIN_CRITICAL;
+dequeue_free_scb:
+       mov     SCBPTR, FREE_SCBH;
+       mov     FREE_SCBH, SCB_NEXT ret;
+END_CRITICAL;
+
+add_scb_to_disc_list:
+/*
+ * Link this SCB into the DISCONNECTED list.  This list holds the
+ * candidates for paging out an SCB if one is needed for a new command.
+ * Modifying the disconnected list is a critical(pause dissabled) section.
+ */
+BEGIN_CRITICAL;
+       mov     SCB_NEXT, DISCONNECTED_SCBH;
+       mov     DISCONNECTED_SCBH, SCBPTR ret;
+END_CRITICAL;
+}
+set_seqint:
+       mov     INTSTAT, SINDEX;
+       nop;
+return:
+       ret;
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.c b/xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
new file mode 100644 (file)
index 0000000..0ae6bde
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ * Interface for the 93C66/56/46/26/06 serial eeprom parts.
+ *
+ * Copyright (c) 1995, 1996 Daniel M. Eischen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_93cx6.c#15 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_93cx6.c,v 1.8.2.5 2002/04/29 19:36:31 gibbs Exp $
+ */
+
+/*
+ *   The instruction set of the 93C66/56/46/26/06 chips are as follows:
+ *
+ *               Start  OP         *
+ *     Function   Bit  Code  Address**  Data     Description
+ *     -------------------------------------------------------------------
+ *     READ        1    10   A5 - A0             Reads data stored in memory,
+ *                                               starting at specified address
+ *     EWEN        1    00   11XXXX              Write enable must precede
+ *                                               all programming modes
+ *     ERASE       1    11   A5 - A0             Erase register A5A4A3A2A1A0
+ *     WRITE       1    01   A5 - A0   D15 - D0  Writes register
+ *     ERAL        1    00   10XXXX              Erase all registers
+ *     WRAL        1    00   01XXXX    D15 - D0  Writes to all registers
+ *     EWDS        1    00   00XXXX              Disables all programming
+ *                                               instructions
+ *     *Note: A value of X for address is a don't care condition.
+ *    **Note: There are 8 address bits for the 93C56/66 chips unlike
+ *           the 93C46/26/06 chips which have 6 address bits.
+ *
+ *   The 93C46 has a four wire interface: clock, chip select, data in, and
+ *   data out.  In order to perform one of the above functions, you need
+ *   to enable the chip select for a clock period (typically a minimum of
+ *   1 usec, with the clock high and low a minimum of 750 and 250 nsec
+ *   respectively).  While the chip select remains high, you can clock in
+ *   the instructions (above) starting with the start bit, followed by the
+ *   OP code, Address, and Data (if needed).  For the READ instruction, the
+ *   requested 16-bit register contents is read from the data out line but
+ *   is preceded by an initial zero (leading 0, followed by 16-bits, MSB
+ *   first).  The clock cycling from low to high initiates the next data
+ *   bit to be sent from the chip.
+ *
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aic7xxx_93cx6.h>
+#endif
+
+/*
+ * Right now, we only have to read the SEEPROM.  But we make it easier to
+ * add other 93Cx6 functions.
+ */
+static struct seeprom_cmd {
+       uint8_t len;
+       uint8_t bits[9];
+} seeprom_read = {3, {1, 1, 0}};
+
+static struct seeprom_cmd seeprom_ewen = {9, {1, 0, 0, 1, 1, 0, 0, 0, 0}};
+static struct seeprom_cmd seeprom_ewds = {9, {1, 0, 0, 0, 0, 0, 0, 0, 0}};
+static struct seeprom_cmd seeprom_write = {3, {1, 0, 1}};
+
+/*
+ * Wait for the SEERDY to go high; about 800 ns.
+ */
+#define CLOCK_PULSE(sd, rdy)                           \
+       while ((SEEPROM_STATUS_INB(sd) & rdy) == 0) {   \
+               ;  /* Do nothing */                     \
+       }                                               \
+       (void)SEEPROM_INB(sd);  /* Clear clock */
+
+/*
+ * Send a START condition and the given command
+ */
+static void
+send_seeprom_cmd(struct seeprom_descriptor *sd, struct seeprom_cmd *cmd)
+{
+       uint8_t temp;
+       int i = 0;
+
+       /* Send chip select for one clock cycle. */
+       temp = sd->sd_MS ^ sd->sd_CS;
+       SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+       CLOCK_PULSE(sd, sd->sd_RDY);
+
+       for (i = 0; i < cmd->len; i++) {
+               if (cmd->bits[i] != 0)
+                       temp ^= sd->sd_DO;
+               SEEPROM_OUTB(sd, temp);
+               CLOCK_PULSE(sd, sd->sd_RDY);
+               SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+               CLOCK_PULSE(sd, sd->sd_RDY);
+               if (cmd->bits[i] != 0)
+                       temp ^= sd->sd_DO;
+       }
+}
+
+/*
+ * Clear CS put the chip in the reset state, where it can wait for new commands.
+ */
+static void
+reset_seeprom(struct seeprom_descriptor *sd)
+{
+       uint8_t temp;
+
+       temp = sd->sd_MS;
+       SEEPROM_OUTB(sd, temp);
+       CLOCK_PULSE(sd, sd->sd_RDY);
+       SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+       CLOCK_PULSE(sd, sd->sd_RDY);
+       SEEPROM_OUTB(sd, temp);
+       CLOCK_PULSE(sd, sd->sd_RDY);
+}
+
+/*
+ * Read the serial EEPROM and returns 1 if successful and 0 if
+ * not successful.
+ */
+int
+ahc_read_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+                u_int start_addr, u_int count)
+{
+       int i = 0;
+       u_int k = 0;
+       uint16_t v;
+       uint8_t temp;
+
+       /*
+        * Read the requested registers of the seeprom.  The loop
+        * will range from 0 to count-1.
+        */
+       for (k = start_addr; k < count + start_addr; k++) {
+               /*
+                * Now we're ready to send the read command followed by the
+                * address of the 16-bit register we want to read.
+                */
+               send_seeprom_cmd(sd, &seeprom_read);
+
+               /* Send the 6 or 8 bit address (MSB first, LSB last). */
+               temp = sd->sd_MS ^ sd->sd_CS;
+               for (i = (sd->sd_chip - 1); i >= 0; i--) {
+                       if ((k & (1 << i)) != 0)
+                               temp ^= sd->sd_DO;
+                       SEEPROM_OUTB(sd, temp);
+                       CLOCK_PULSE(sd, sd->sd_RDY);
+                       SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+                       CLOCK_PULSE(sd, sd->sd_RDY);
+                       if ((k & (1 << i)) != 0)
+                               temp ^= sd->sd_DO;
+               }
+
+               /*
+                * Now read the 16 bit register.  An initial 0 precedes the
+                * register contents which begins with bit 15 (MSB) and ends
+                * with bit 0 (LSB).  The initial 0 will be shifted off the
+                * top of our word as we let the loop run from 0 to 16.
+                */
+               v = 0;
+               for (i = 16; i >= 0; i--) {
+                       SEEPROM_OUTB(sd, temp);
+                       CLOCK_PULSE(sd, sd->sd_RDY);
+                       v <<= 1;
+                       if (SEEPROM_DATA_INB(sd) & sd->sd_DI)
+                               v |= 1;
+                       SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+                       CLOCK_PULSE(sd, sd->sd_RDY);
+               }
+
+               buf[k - start_addr] = v;
+
+               /* Reset the chip select for the next command cycle. */
+               reset_seeprom(sd);
+       }
+#ifdef AHC_DUMP_EEPROM
+       printf("\nSerial EEPROM:\n\t");
+       for (k = 0; k < count; k = k + 1) {
+               if (((k % 8) == 0) && (k != 0)) {
+                       printf ("\n\t");
+               }
+               printf (" 0x%x", buf[k]);
+       }
+       printf ("\n");
+#endif
+       return (1);
+}
+
+/*
+ * Write the serial EEPROM and return 1 if successful and 0 if
+ * not successful.
+ */
+int
+ahc_write_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+                 u_int start_addr, u_int count)
+{
+       uint16_t v;
+       uint8_t temp;
+       int i, k;
+
+       /* Place the chip into write-enable mode */
+       send_seeprom_cmd(sd, &seeprom_ewen);
+       reset_seeprom(sd);
+
+       /* Write all requested data out to the seeprom. */
+       temp = sd->sd_MS ^ sd->sd_CS;
+       for (k = start_addr; k < count + start_addr; k++) {
+               /* Send the write command */
+               send_seeprom_cmd(sd, &seeprom_write);
+
+               /* Send the 6 or 8 bit address (MSB first). */
+               for (i = (sd->sd_chip - 1); i >= 0; i--) {
+                       if ((k & (1 << i)) != 0)
+                               temp ^= sd->sd_DO;
+                       SEEPROM_OUTB(sd, temp);
+                       CLOCK_PULSE(sd, sd->sd_RDY);
+                       SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+                       CLOCK_PULSE(sd, sd->sd_RDY);
+                       if ((k & (1 << i)) != 0)
+                               temp ^= sd->sd_DO;
+               }
+
+               /* Write the 16 bit value, MSB first */
+               v = buf[k - start_addr];
+               for (i = 15; i >= 0; i--) {
+                       if ((v & (1 << i)) != 0)
+                               temp ^= sd->sd_DO;
+                       SEEPROM_OUTB(sd, temp);
+                       CLOCK_PULSE(sd, sd->sd_RDY);
+                       SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+                       CLOCK_PULSE(sd, sd->sd_RDY);
+                       if ((v & (1 << i)) != 0)
+                               temp ^= sd->sd_DO;
+               }
+
+               /* Wait for the chip to complete the write */
+               temp = sd->sd_MS;
+               SEEPROM_OUTB(sd, temp);
+               CLOCK_PULSE(sd, sd->sd_RDY);
+               temp = sd->sd_MS ^ sd->sd_CS;
+               do {
+                       SEEPROM_OUTB(sd, temp);
+                       CLOCK_PULSE(sd, sd->sd_RDY);
+                       SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+                       CLOCK_PULSE(sd, sd->sd_RDY);
+               } while ((SEEPROM_DATA_INB(sd) & sd->sd_DI) == 0);
+
+               reset_seeprom(sd);
+       }
+
+       /* Put the chip back into write-protect mode */
+       send_seeprom_cmd(sd, &seeprom_ewds);
+       reset_seeprom(sd);
+
+       return (1);
+}
+
+int
+ahc_verify_cksum(struct seeprom_config *sc)
+{
+       int i;
+       int maxaddr;
+       uint32_t checksum;
+       uint16_t *scarray;
+
+       maxaddr = (sizeof(*sc)/2) - 1;
+       checksum = 0;
+       scarray = (uint16_t *)sc;
+
+       for (i = 0; i < maxaddr; i++)
+               checksum = checksum + scarray[i];
+       if (checksum == 0
+        || (checksum & 0xFFFF) != sc->checksum) {
+               return (0);
+       } else {
+               return(1);
+       }
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.h b/xen/drivers/scsi/aic7xxx/aic7xxx_93cx6.h
new file mode 100644 (file)
index 0000000..9535c72
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Interface to the 93C46/56 serial EEPROM that is used to store BIOS
+ * settings for the aic7xxx based adaptec SCSI controllers.  It can
+ * also be used for 93C26 and 93C06 serial EEPROMS.
+ *
+ * Copyright (c) 1994, 1995, 2000 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_93cx6.h#10 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_93cx6.h,v 1.7.2.3 2002/04/29 19:36:31 gibbs Exp $
+ */
+#ifndef _AIC7XXX_93CX6_H_
+#define _AIC7XXX_93CX6_H_
+
+typedef enum {
+       C46 = 6,
+       C56_66 = 8
+} seeprom_chip_t;
+
+struct seeprom_descriptor {
+       struct ahc_softc *sd_ahc;
+       u_int sd_control_offset;
+       u_int sd_status_offset;
+       u_int sd_dataout_offset;
+       seeprom_chip_t sd_chip;
+       uint16_t sd_MS;
+       uint16_t sd_RDY;
+       uint16_t sd_CS;
+       uint16_t sd_CK;
+       uint16_t sd_DO;
+       uint16_t sd_DI;
+};
+
+/*
+ * This function will read count 16-bit words from the serial EEPROM and
+ * return their value in buf.  The port address of the aic7xxx serial EEPROM
+ * control register is passed in as offset.  The following parameters are
+ * also passed in:
+ *
+ *   CS  - Chip select
+ *   CK  - Clock
+ *   DO  - Data out
+ *   DI  - Data in
+ *   RDY - SEEPROM ready
+ *   MS  - Memory port mode select
+ *
+ *  A failed read attempt returns 0, and a successful read returns 1.
+ */
+
+#define        SEEPROM_INB(sd) \
+       ahc_inb(sd->sd_ahc, sd->sd_control_offset)
+#define        SEEPROM_OUTB(sd, value)                                 \
+do {                                                           \
+       ahc_outb(sd->sd_ahc, sd->sd_control_offset, value);     \
+       ahc_flush_device_writes(sd->sd_ahc);                    \
+} while(0)
+
+#define        SEEPROM_STATUS_INB(sd) \
+       ahc_inb(sd->sd_ahc, sd->sd_status_offset)
+#define        SEEPROM_DATA_INB(sd) \
+       ahc_inb(sd->sd_ahc, sd->sd_dataout_offset)
+
+int ahc_read_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+                    u_int start_addr, u_int count);
+int ahc_write_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+                     u_int start_addr, u_int count);
+int ahc_verify_cksum(struct seeprom_config *sc);
+
+#endif /* _AIC7XXX_93CX6_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_core.c b/xen/drivers/scsi/aic7xxx/aic7xxx_core.c
new file mode 100644 (file)
index 0000000..ebbf161
--- /dev/null
@@ -0,0 +1,7127 @@
+/*
+ * Core routines and tables shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.c#69 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.c,v 1.41.2.22 2002/04/29 19:36:26 gibbs Exp $
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aicasm/aicasm_insformat.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aicasm/aicasm_insformat.h>
+#endif
+
+/****************************** Softc Data ************************************/
+struct ahc_softc_tailq ahc_tailq = TAILQ_HEAD_INITIALIZER(ahc_tailq);
+
+/***************************** Lookup Tables **********************************/
+char *ahc_chip_names[] =
+{
+       "NONE",
+       "aic7770",
+       "aic7850",
+       "aic7855",
+       "aic7859",
+       "aic7860",
+       "aic7870",
+       "aic7880",
+       "aic7895",
+       "aic7895C",
+       "aic7890/91",
+       "aic7896/97",
+       "aic7892",
+       "aic7899"
+};
+static const u_int num_chip_names = NUM_ELEMENTS(ahc_chip_names);
+
+/*
+ * Hardware error codes.
+ */
+struct ahc_hard_error_entry {
+        uint8_t errno;
+       char *errmesg;
+};
+
+static struct ahc_hard_error_entry ahc_hard_errors[] = {
+       { ILLHADDR,     "Illegal Host Access" },
+       { ILLSADDR,     "Illegal Sequencer Address referrenced" },
+       { ILLOPCODE,    "Illegal Opcode in sequencer program" },
+       { SQPARERR,     "Sequencer Parity Error" },
+       { DPARERR,      "Data-path Parity Error" },
+       { MPARERR,      "Scratch or SCB Memory Parity Error" },
+       { PCIERRSTAT,   "PCI Error detected" },
+       { CIOPARERR,    "CIOBUS Parity Error" },
+};
+static const u_int num_errors = NUM_ELEMENTS(ahc_hard_errors);
+
+static struct ahc_phase_table_entry ahc_phase_table[] =
+{
+       { P_DATAOUT,    MSG_NOOP,               "in Data-out phase"     },
+       { P_DATAIN,     MSG_INITIATOR_DET_ERR,  "in Data-in phase"      },
+       { P_DATAOUT_DT, MSG_NOOP,               "in DT Data-out phase"  },
+       { P_DATAIN_DT,  MSG_INITIATOR_DET_ERR,  "in DT Data-in phase"   },
+       { P_COMMAND,    MSG_NOOP,               "in Command phase"      },
+       { P_MESGOUT,    MSG_NOOP,               "in Message-out phase"  },
+       { P_STATUS,     MSG_INITIATOR_DET_ERR,  "in Status phase"       },
+       { P_MESGIN,     MSG_PARITY_ERROR,       "in Message-in phase"   },
+       { P_BUSFREE,    MSG_NOOP,               "while idle"            },
+       { 0,            MSG_NOOP,               "in unknown phase"      }
+};
+
+/*
+ * In most cases we only wish to itterate over real phases, so
+ * exclude the last element from the count.
+ */
+static const u_int num_phases = NUM_ELEMENTS(ahc_phase_table) - 1;
+
+/*
+ * Valid SCSIRATE values.  (p. 3-17)
+ * Provides a mapping of tranfer periods in ns to the proper value to
+ * stick in the scsixfer reg.
+ */
+static struct ahc_syncrate ahc_syncrates[] =
+{
+      /* ultra2    fast/ultra  period     rate */
+       { 0x42,      0x000,      9,      "80.0" },
+       { 0x03,      0x000,     10,      "40.0" },
+       { 0x04,      0x000,     11,      "33.0" },
+       { 0x05,      0x100,     12,      "20.0" },
+       { 0x06,      0x110,     15,      "16.0" },
+       { 0x07,      0x120,     18,      "13.4" },
+       { 0x08,      0x000,     25,      "10.0" },
+       { 0x19,      0x010,     31,      "8.0"  },
+       { 0x1a,      0x020,     37,      "6.67" },
+       { 0x1b,      0x030,     43,      "5.7"  },
+       { 0x1c,      0x040,     50,      "5.0"  },
+       { 0x00,      0x050,     56,      "4.4"  },
+       { 0x00,      0x060,     62,      "4.0"  },
+       { 0x00,      0x070,     68,      "3.6"  },
+       { 0x00,      0x000,      0,      NULL   }
+};
+
+/* Our Sequencer Program */
+#include "aic7xxx_seq.h"
+
+/**************************** Function Declarations ***************************/
+static void            ahc_force_renegotiation(struct ahc_softc *ahc);
+static struct ahc_tmode_tstate*
+                       ahc_alloc_tstate(struct ahc_softc *ahc,
+                                        u_int scsi_id, char channel);
+#ifdef AHC_TARGET_MODE
+static void            ahc_free_tstate(struct ahc_softc *ahc,
+                                       u_int scsi_id, char channel, int force);
+#endif
+static struct ahc_syncrate*
+                       ahc_devlimited_syncrate(struct ahc_softc *ahc,
+                                               struct ahc_initiator_tinfo *,
+                                               u_int *period,
+                                               u_int *ppr_options,
+                                               role_t role);
+static void            ahc_update_pending_scbs(struct ahc_softc *ahc);
+static void            ahc_fetch_devinfo(struct ahc_softc *ahc,
+                                         struct ahc_devinfo *devinfo);
+static void            ahc_scb_devinfo(struct ahc_softc *ahc,
+                                       struct ahc_devinfo *devinfo,
+                                       struct scb *scb);
+static void            ahc_assert_atn(struct ahc_softc *ahc);
+static void            ahc_setup_initiator_msgout(struct ahc_softc *ahc,
+                                                  struct ahc_devinfo *devinfo,
+                                                  struct scb *scb);
+static void            ahc_build_transfer_msg(struct ahc_softc *ahc,
+                                              struct ahc_devinfo *devinfo);
+static void            ahc_construct_sdtr(struct ahc_softc *ahc,
+                                          struct ahc_devinfo *devinfo,
+                                          u_int period, u_int offset);
+static void            ahc_construct_wdtr(struct ahc_softc *ahc,
+                                          struct ahc_devinfo *devinfo,
+                                          u_int bus_width);
+static void            ahc_construct_ppr(struct ahc_softc *ahc,
+                                         struct ahc_devinfo *devinfo,
+                                         u_int period, u_int offset,
+                                         u_int bus_width, u_int ppr_options);
+static void            ahc_clear_msg_state(struct ahc_softc *ahc);
+static void            ahc_handle_message_phase(struct ahc_softc *ahc);
+typedef enum {
+       AHCMSG_1B,
+       AHCMSG_2B,
+       AHCMSG_EXT
+} ahc_msgtype;
+static int             ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type,
+                                    u_int msgval, int full);
+static int             ahc_parse_msg(struct ahc_softc *ahc,
+                                     struct ahc_devinfo *devinfo);
+static int             ahc_handle_msg_reject(struct ahc_softc *ahc,
+                                             struct ahc_devinfo *devinfo);
+static void            ahc_handle_ign_wide_residue(struct ahc_softc *ahc,
+                                               struct ahc_devinfo *devinfo);
+static void            ahc_reinitialize_dataptrs(struct ahc_softc *ahc);
+static void            ahc_handle_devreset(struct ahc_softc *ahc,
+                                           struct ahc_devinfo *devinfo,
+                                           cam_status status, char *message,
+                                           int verbose_level);
+#if AHC_TARGET_MODE
+static void            ahc_setup_target_msgin(struct ahc_softc *ahc,
+                                              struct ahc_devinfo *devinfo,
+                                              struct scb *scb);
+#endif
+
+static bus_dmamap_callback_t   ahc_dmamap_cb; 
+static void                    ahc_build_free_scb_list(struct ahc_softc *ahc);
+static int                     ahc_init_scbdata(struct ahc_softc *ahc);
+static void                    ahc_fini_scbdata(struct ahc_softc *ahc);
+static void            ahc_qinfifo_requeue(struct ahc_softc *ahc,
+                                           struct scb *prev_scb,
+                                           struct scb *scb);
+static int             ahc_qinfifo_count(struct ahc_softc *ahc);
+static u_int           ahc_rem_scb_from_disc_list(struct ahc_softc *ahc,
+                                                  u_int prev, u_int scbptr);
+static void            ahc_add_curscb_to_free_list(struct ahc_softc *ahc);
+static u_int           ahc_rem_wscb(struct ahc_softc *ahc,
+                                    u_int scbpos, u_int prev);
+static void            ahc_reset_current_bus(struct ahc_softc *ahc);
+#ifdef AHC_DUMP_SEQ
+static void            ahc_dumpseq(struct ahc_softc *ahc);
+#endif
+static void            ahc_loadseq(struct ahc_softc *ahc);
+static int             ahc_check_patch(struct ahc_softc *ahc,
+                                       struct patch **start_patch,
+                                       u_int start_instr, u_int *skip_addr);
+static void            ahc_download_instr(struct ahc_softc *ahc,
+                                          u_int instrptr, uint8_t *dconsts);
+#ifdef AHC_TARGET_MODE
+static void            ahc_queue_lstate_event(struct ahc_softc *ahc,
+                                              struct ahc_tmode_lstate *lstate,
+                                              u_int initiator_id,
+                                              u_int event_type,
+                                              u_int event_arg);
+static void            ahc_update_scsiid(struct ahc_softc *ahc,
+                                         u_int targid_mask);
+static int             ahc_handle_target_cmd(struct ahc_softc *ahc,
+                                             struct target_cmd *cmd);
+#endif
+/************************* Sequencer Execution Control ************************/
+/*
+ * Restart the sequencer program from address zero
+ */
+void
+ahc_restart(struct ahc_softc *ahc)
+{
+
+       ahc_pause(ahc);
+
+       /* No more pending messages. */
+       ahc_clear_msg_state(ahc);
+
+       ahc_outb(ahc, SCSISIGO, 0);             /* De-assert BSY */
+       ahc_outb(ahc, MSG_OUT, MSG_NOOP);       /* No message to send */
+       ahc_outb(ahc, SXFRCTL1, ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
+       ahc_outb(ahc, LASTPHASE, P_BUSFREE);
+       ahc_outb(ahc, SAVED_SCSIID, 0xFF);
+       ahc_outb(ahc, SAVED_LUN, 0xFF);
+
+       /*
+        * Ensure that the sequencer's idea of TQINPOS
+        * matches our own.  The sequencer increments TQINPOS
+        * only after it sees a DMA complete and a reset could
+        * occur before the increment leaving the kernel to believe
+        * the command arrived but the sequencer to not.
+        */
+       ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
+
+       /* Always allow reselection */
+       ahc_outb(ahc, SCSISEQ,
+                ahc_inb(ahc, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
+       if ((ahc->features & AHC_CMD_CHAN) != 0) {
+               /* Ensure that no DMA operations are in progress */
+               ahc_outb(ahc, CCSCBCNT, 0);
+               ahc_outb(ahc, CCSGCTL, 0);
+               ahc_outb(ahc, CCSCBCTL, 0);
+       }
+       /*
+        * If we were in the process of DMA'ing SCB data into
+        * an SCB, replace that SCB on the free list.  This prevents
+        * an SCB leak.
+        */
+       if ((ahc_inb(ahc, SEQ_FLAGS2) & SCB_DMA) != 0) {
+               ahc_add_curscb_to_free_list(ahc);
+               ahc_outb(ahc, SEQ_FLAGS2,
+                        ahc_inb(ahc, SEQ_FLAGS2) & ~SCB_DMA);
+       }
+       ahc_outb(ahc, MWI_RESIDUAL, 0);
+       ahc_outb(ahc, SEQCTL, FASTMODE);
+       ahc_outb(ahc, SEQADDR0, 0);
+       ahc_outb(ahc, SEQADDR1, 0);
+       ahc_unpause(ahc);
+}
+
+/************************* Input/Output Queues ********************************/
+void
+ahc_run_qoutfifo(struct ahc_softc *ahc)
+{
+       struct scb *scb;
+       u_int  scb_index;
+
+       ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
+       while (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL) {
+
+               scb_index = ahc->qoutfifo[ahc->qoutfifonext];
+               if ((ahc->qoutfifonext & 0x03) == 0x03) {
+                       u_int modnext;
+
+                       /*
+                        * Clear 32bits of QOUTFIFO at a time
+                        * so that we don't clobber an incoming
+                        * byte DMA to the array on architectures
+                        * that only support 32bit load and store
+                        * operations.
+                        */
+                       modnext = ahc->qoutfifonext & ~0x3;
+                       *((uint32_t *)(&ahc->qoutfifo[modnext])) = 0xFFFFFFFFUL;
+                       ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+                                       ahc->shared_data_dmamap,
+                                       /*offset*/modnext, /*len*/4,
+                                       BUS_DMASYNC_PREREAD);
+               }
+               ahc->qoutfifonext++;
+
+               scb = ahc_lookup_scb(ahc, scb_index);
+               if (scb == NULL) {
+                       printf("%s: WARNING no command for scb %d "
+                              "(cmdcmplt)\nQOUTPOS = %d\n",
+                              ahc_name(ahc), scb_index,
+                              ahc->qoutfifonext - 1);
+                       continue;
+               }
+
+               /*
+                * Save off the residual
+                * if there is one.
+                */
+               ahc_update_residual(ahc, scb);
+               ahc_done(ahc, scb);
+       }
+}
+
+void
+ahc_run_untagged_queues(struct ahc_softc *ahc)
+{
+       int i;
+
+       for (i = 0; i < 16; i++)
+               ahc_run_untagged_queue(ahc, &ahc->untagged_queues[i]);
+}
+
+void
+ahc_run_untagged_queue(struct ahc_softc *ahc, struct scb_tailq *queue)
+{
+       struct scb *scb;
+
+       if (ahc->untagged_queue_lock != 0)
+               return;
+
+       if ((scb = TAILQ_FIRST(queue)) != NULL
+        && (scb->flags & SCB_ACTIVE) == 0) {
+               scb->flags |= SCB_ACTIVE;
+               ahc_queue_scb(ahc, scb);
+       }
+}
+
+/************************* Interrupt Handling *********************************/
+void
+ahc_handle_brkadrint(struct ahc_softc *ahc)
+{
+       /*
+        * We upset the sequencer :-(
+        * Lookup the error message
+        */
+       int i;
+       int error;
+
+       error = ahc_inb(ahc, ERROR);
+       for (i = 0; error != 1 && i < num_errors; i++)
+               error >>= 1;
+       printf("%s: brkadrint, %s at seqaddr = 0x%x\n",
+              ahc_name(ahc), ahc_hard_errors[i].errmesg,
+              ahc_inb(ahc, SEQADDR0) |
+              (ahc_inb(ahc, SEQADDR1) << 8));
+
+       ahc_dump_card_state(ahc);
+
+       /* Tell everyone that this HBA is no longer availible */
+       ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+                      CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
+                      CAM_NO_HBA);
+
+       /* Disable all interrupt sources by resetting the controller */
+       ahc_shutdown(ahc);
+}
+
+void
+ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
+{
+       struct scb *scb;
+       struct ahc_devinfo devinfo;
+       
+       ahc_fetch_devinfo(ahc, &devinfo);
+
+       /*
+        * Clear the upper byte that holds SEQINT status
+        * codes and clear the SEQINT bit. We will unpause
+        * the sequencer, if appropriate, after servicing
+        * the request.
+        */
+       ahc_outb(ahc, CLRINT, CLRSEQINT);
+       switch (intstat & SEQINT_MASK) {
+       case BAD_STATUS:
+       {
+               u_int  scb_index;
+               struct hardware_scb *hscb;
+
+               /*
+                * Set the default return value to 0 (don't
+                * send sense).  The sense code will change
+                * this if needed.
+                */
+               ahc_outb(ahc, RETURN_1, 0);
+
+               /*
+                * The sequencer will notify us when a command
+                * has an error that would be of interest to
+                * the kernel.  This allows us to leave the sequencer
+                * running in the common case of command completes
+                * without error.  The sequencer will already have
+                * dma'd the SCB back up to us, so we can reference
+                * the in kernel copy directly.
+                */
+               scb_index = ahc_inb(ahc, SCB_TAG);
+               scb = ahc_lookup_scb(ahc, scb_index);
+               if (scb == NULL) {
+                       printf("%s:%c:%d: ahc_intr - referenced scb "
+                              "not valid during seqint 0x%x scb(%d)\n",
+                              ahc_name(ahc), devinfo.channel,
+                              devinfo.target, intstat, scb_index);
+                       ahc_dump_card_state(ahc);
+                       panic("for safety");
+                       goto unpause;
+               }
+
+               hscb = scb->hscb; 
+
+               /* Don't want to clobber the original sense code */
+               if ((scb->flags & SCB_SENSE) != 0) {
+                       /*
+                        * Clear the SCB_SENSE Flag and have
+                        * the sequencer do a normal command
+                        * complete.
+                        */
+                       scb->flags &= ~SCB_SENSE;
+                       ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+                       break;
+               }
+               ahc_set_transaction_status(scb, CAM_SCSI_STATUS_ERROR);
+               /* Freeze the queue until the client sees the error. */
+               ahc_freeze_devq(ahc, scb);
+               ahc_freeze_scb(scb);
+               ahc_set_scsi_status(scb, hscb->shared_data.status.scsi_status);
+               switch (hscb->shared_data.status.scsi_status) {
+               case SCSI_STATUS_OK:
+                       printf("%s: Interrupted for staus of 0???\n",
+                              ahc_name(ahc));
+                       break;
+               case SCSI_STATUS_CMD_TERMINATED:
+               case SCSI_STATUS_CHECK_COND:
+               {
+                       struct ahc_dma_seg *sg;
+                       struct scsi_sense *sc;
+                       struct ahc_initiator_tinfo *targ_info;
+                       struct ahc_tmode_tstate *tstate;
+                       struct ahc_transinfo *tinfo;
+#ifdef AHC_DEBUG
+                       if (ahc_debug & AHC_SHOWSENSE) {
+                               ahc_print_path(ahc, scb);
+                               printf("SCB %d: requests Check Status\n",
+                                      scb->hscb->tag);
+                       }
+#endif
+
+                       if (ahc_perform_autosense(scb) == 0)
+                               break;
+
+                       targ_info = ahc_fetch_transinfo(ahc,
+                                                       devinfo.channel,
+                                                       devinfo.our_scsiid,
+                                                       devinfo.target,
+                                                       &tstate);
+                       tinfo = &targ_info->curr;
+                       sg = scb->sg_list;
+                       sc = (struct scsi_sense *)(&hscb->shared_data.cdb); 
+                       /*
+                        * Save off the residual if there is one.
+                        */
+                       ahc_update_residual(ahc, scb);
+#ifdef AHC_DEBUG
+                       if (ahc_debug & AHC_SHOWSENSE) {
+                               ahc_print_path(ahc, scb);
+                               printf("Sending Sense\n");
+                       }
+#endif
+                       sg->addr = ahc_get_sense_bufaddr(ahc, scb);
+                       sg->len = ahc_get_sense_bufsize(ahc, scb);
+                       sg->len |= AHC_DMA_LAST_SEG;
+
+                       /* Fixup byte order */
+                       sg->addr = ahc_htole32(sg->addr);
+                       sg->len = ahc_htole32(sg->len);
+
+                       sc->opcode = REQUEST_SENSE;
+                       sc->byte2 = 0;
+                       if (tinfo->protocol_version <= SCSI_REV_2
+                        && SCB_GET_LUN(scb) < 8)
+                               sc->byte2 = SCB_GET_LUN(scb) << 5;
+                       sc->unused[0] = 0;
+                       sc->unused[1] = 0;
+                       sc->length = sg->len;
+                       sc->control = 0;
+
+                       /*
+                        * We can't allow the target to disconnect.
+                        * This will be an untagged transaction and
+                        * having the target disconnect will make this
+                        * transaction indestinguishable from outstanding
+                        * tagged transactions.
+                        */
+                       hscb->control = 0;
+
+                       /*
+                        * This request sense could be because the
+                        * the device lost power or in some other
+                        * way has lost our transfer negotiations.
+                        * Renegotiate if appropriate.  Unit attention
+                        * errors will be reported before any data
+                        * phases occur.
+                        */
+                       if (ahc_get_residual(scb) 
+                        == ahc_get_transfer_length(scb)) {
+                               ahc_update_neg_request(ahc, &devinfo,
+                                                      tstate, targ_info,
+                                                      /*force*/TRUE);
+                       }
+                       if (tstate->auto_negotiate & devinfo.target_mask) {
+                               hscb->control |= MK_MESSAGE;
+                               scb->flags &= ~SCB_NEGOTIATE;
+                               scb->flags |= SCB_AUTO_NEGOTIATE;
+                       }
+                       hscb->cdb_len = sizeof(*sc);
+                       hscb->dataptr = sg->addr; 
+                       hscb->datacnt = sg->len;
+                       hscb->sgptr = scb->sg_list_phys | SG_FULL_RESID;
+                       hscb->sgptr = ahc_htole32(hscb->sgptr);
+                       scb->sg_count = 1;
+                       scb->flags |= SCB_SENSE;
+                       ahc_qinfifo_requeue_tail(ahc, scb);
+                       ahc_outb(ahc, RETURN_1, SEND_SENSE);
+#ifdef __FreeBSD__
+                       /*
+                        * Ensure we have enough time to actually
+                        * retrieve the sense.
+                        */
+                       untimeout(ahc_timeout, (caddr_t)scb,
+                                 scb->io_ctx->ccb_h.timeout_ch);
+                       scb->io_ctx->ccb_h.timeout_ch =
+                           timeout(ahc_timeout, (caddr_t)scb, 5 * hz);
+#endif
+                       break;
+               }
+               default:
+                       break;
+               }
+               break;
+       }
+       case NO_MATCH:
+       {
+               /* Ensure we don't leave the selection hardware on */
+               ahc_outb(ahc, SCSISEQ,
+                        ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+
+               printf("%s:%c:%d: no active SCB for reconnecting "
+                      "target - issuing BUS DEVICE RESET\n",
+                      ahc_name(ahc), devinfo.channel, devinfo.target);
+               printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+                      "ARG_1 == 0x%x ACCUM = 0x%x\n",
+                      ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
+                      ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
+               printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+                      "SINDEX == 0x%x\n",
+                      ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
+                      ahc_index_busy_tcl(ahc,
+                           BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
+                                     ahc_inb(ahc, SAVED_LUN))),
+                      ahc_inb(ahc, SINDEX));
+               printf("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+                      "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
+                      ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
+                      ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
+                      ahc_inb(ahc, SCB_CONTROL));
+               printf("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
+                      ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
+               printf("SXFRCTL0 == 0x%x\n", ahc_inb(ahc, SXFRCTL0));
+               printf("SEQCTL == 0x%x\n", ahc_inb(ahc, SEQCTL));
+               ahc_dump_card_state(ahc);
+               ahc->msgout_buf[0] = MSG_BUS_DEV_RESET;
+               ahc->msgout_len = 1;
+               ahc->msgout_index = 0;
+               ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+               ahc_outb(ahc, MSG_OUT, HOST_MSG);
+               ahc_assert_atn(ahc);
+               break;
+       }
+       case SEND_REJECT: 
+       {
+               u_int rejbyte = ahc_inb(ahc, ACCUM);
+               printf("%s:%c:%d: Warning - unknown message received from "
+                      "target (0x%x).  Rejecting\n", 
+                      ahc_name(ahc), devinfo.channel, devinfo.target, rejbyte);
+               break; 
+       }
+       case NO_IDENT: 
+       {
+               /*
+                * The reconnecting target either did not send an identify
+                * message, or did, but we didn't find an SCB to match and
+                * before it could respond to our ATN/abort, it hit a dataphase.
+                * The only safe thing to do is to blow it away with a bus
+                * reset.
+                */
+               int found;
+
+               printf("%s:%c:%d: Target did not send an IDENTIFY message. "
+                      "LASTPHASE = 0x%x, SAVED_SCSIID == 0x%x\n",
+                      ahc_name(ahc), devinfo.channel, devinfo.target,
+                      ahc_inb(ahc, LASTPHASE), ahc_inb(ahc, SAVED_SCSIID));
+               found = ahc_reset_channel(ahc, devinfo.channel, 
+                                         /*initiate reset*/TRUE);
+               printf("%s: Issued Channel %c Bus Reset. "
+                      "%d SCBs aborted\n", ahc_name(ahc), devinfo.channel,
+                      found);
+               return;
+       }
+       case IGN_WIDE_RES:
+               ahc_handle_ign_wide_residue(ahc, &devinfo);
+               break;
+       case PDATA_REINIT:
+               ahc_reinitialize_dataptrs(ahc);
+               break;
+       case BAD_PHASE:
+       {
+               u_int lastphase;
+
+               lastphase = ahc_inb(ahc, LASTPHASE);
+               printf("%s:%c:%d: unknown scsi bus phase %x, "
+                      "lastphase = 0x%x.  Attempting to continue\n",
+                      ahc_name(ahc), devinfo.channel, devinfo.target,
+                      lastphase, ahc_inb(ahc, SCSISIGI));
+               break;
+       }
+       case MISSED_BUSFREE:
+       {
+               u_int lastphase;
+
+               lastphase = ahc_inb(ahc, LASTPHASE);
+               printf("%s:%c:%d: Missed busfree. "
+                      "Lastphase = 0x%x, Curphase = 0x%x\n",
+                      ahc_name(ahc), devinfo.channel, devinfo.target,
+                      lastphase, ahc_inb(ahc, SCSISIGI));
+               ahc_restart(ahc);
+               return;
+       }
+       case HOST_MSG_LOOP:
+       {
+               /*
+                * The sequencer has encountered a message phase
+                * that requires host assistance for completion.
+                * While handling the message phase(s), we will be
+                * notified by the sequencer after each byte is
+                * transfered so we can track bus phase changes.
+                *
+                * If this is the first time we've seen a HOST_MSG_LOOP
+                * interrupt, initialize the state of the host message
+                * loop.
+                */
+               if (ahc->msg_type == MSG_TYPE_NONE) {
+                       struct scb *scb;
+                       u_int scb_index;
+                       u_int bus_phase;
+
+                       bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+                       if (bus_phase != P_MESGIN
+                        && bus_phase != P_MESGOUT) {
+                               printf("ahc_intr: HOST_MSG_LOOP bad "
+                                      "phase 0x%x\n",
+                                     bus_phase);
+                               /*
+                                * Probably transitioned to bus free before
+                                * we got here.  Just punt the message.
+                                */
+                               ahc_clear_intstat(ahc);
+                               ahc_restart(ahc);
+                               return;
+                       }
+
+                       scb_index = ahc_inb(ahc, SCB_TAG);
+                       scb = ahc_lookup_scb(ahc, scb_index);
+                       if (devinfo.role == ROLE_INITIATOR) {
+                               if (scb == NULL)
+                                       panic("HOST_MSG_LOOP with "
+                                             "invalid SCB %x\n", scb_index);
+
+                               if (bus_phase == P_MESGOUT)
+                                       ahc_setup_initiator_msgout(ahc,
+                                                                  &devinfo,
+                                                                  scb);
+                               else {
+                                       ahc->msg_type =
+                                           MSG_TYPE_INITIATOR_MSGIN;
+                                       ahc->msgin_index = 0;
+                               }
+                       }
+#if AHC_TARGET_MODE
+                       else {
+                               if (bus_phase == P_MESGOUT) {
+                                       ahc->msg_type =
+                                           MSG_TYPE_TARGET_MSGOUT;
+                                       ahc->msgin_index = 0;
+                               }
+                               else 
+                                       ahc_setup_target_msgin(ahc,
+                                                              &devinfo,
+                                                              scb);
+                       }
+#endif
+               }
+
+               ahc_handle_message_phase(ahc);
+               break;
+       }
+       case PERR_DETECTED:
+       {
+               /*
+                * If we've cleared the parity error interrupt
+                * but the sequencer still believes that SCSIPERR
+                * is true, it must be that the parity error is
+                * for the currently presented byte on the bus,
+                * and we are not in a phase (data-in) where we will
+                * eventually ack this byte.  Ack the byte and
+                * throw it away in the hope that the target will
+                * take us to message out to deliver the appropriate
+                * error message.
+                */
+               if ((intstat & SCSIINT) == 0
+                && (ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0) {
+
+                       if ((ahc->features & AHC_DT) == 0) {
+                               u_int curphase;
+
+                               /*
+                                * The hardware will only let you ack bytes
+                                * if the expected phase in SCSISIGO matches
+                                * the current phase.  Make sure this is
+                                * currently the case.
+                                */
+                               curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+                               ahc_outb(ahc, LASTPHASE, curphase);
+                               ahc_outb(ahc, SCSISIGO, curphase);
+                       }
+                       ahc_inb(ahc, SCSIDATL);
+               }
+               break;
+       }
+       case DATA_OVERRUN:
+       {
+               /*
+                * When the sequencer detects an overrun, it
+                * places the controller in "BITBUCKET" mode
+                * and allows the target to complete its transfer.
+                * Unfortunately, none of the counters get updated
+                * when the controller is in this mode, so we have
+                * no way of knowing how large the overrun was.
+                */
+               u_int scbindex = ahc_inb(ahc, SCB_TAG);
+               u_int lastphase = ahc_inb(ahc, LASTPHASE);
+               u_int i;
+
+               scb = ahc_lookup_scb(ahc, scbindex);
+               for (i = 0; i < num_phases; i++) {
+                       if (lastphase == ahc_phase_table[i].phase)
+                               break;
+               }
+               ahc_print_path(ahc, scb);
+               printf("data overrun detected %s."
+                      "  Tag == 0x%x.\n",
+                      ahc_phase_table[i].phasemsg,
+                      scb->hscb->tag);
+               ahc_print_path(ahc, scb);
+               printf("%s seen Data Phase.  Length = %ld.  NumSGs = %d.\n",
+                      ahc_inb(ahc, SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+                      ahc_get_transfer_length(scb), scb->sg_count);
+               if (scb->sg_count > 0) {
+                       for (i = 0; i < scb->sg_count; i++) {
+
+                               printf("sg[%d] - Addr 0x%x%x : Length %d\n",
+                                      i,
+                                      (ahc_le32toh(scb->sg_list[i].len) >> 24
+                                       & SG_HIGH_ADDR_BITS),
+                                      ahc_le32toh(scb->sg_list[i].addr),
+                                      ahc_le32toh(scb->sg_list[i].len)
+                                      & AHC_SG_LEN_MASK);
+                       }
+               }
+               /*
+                * Set this and it will take effect when the
+                * target does a command complete.
+                */
+               ahc_freeze_devq(ahc, scb);
+               if ((scb->flags & SCB_SENSE) == 0) {
+                       ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+               } else {
+                       scb->flags &= ~SCB_SENSE;
+                       ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+               }
+               ahc_freeze_scb(scb);
+
+               if ((ahc->features & AHC_ULTRA2) != 0) {
+                       /*
+                        * Clear the channel in case we return
+                        * to data phase later.
+                        */
+                       ahc_outb(ahc, SXFRCTL0,
+                                ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
+                       ahc_outb(ahc, SXFRCTL0,
+                                ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
+               }
+               if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+                       u_int dscommand1;
+
+                       /* Ensure HHADDR is 0 for future DMA operations. */
+                       dscommand1 = ahc_inb(ahc, DSCOMMAND1);
+                       ahc_outb(ahc, DSCOMMAND1, dscommand1 | HADDLDSEL0);
+                       ahc_outb(ahc, HADDR, 0);
+                       ahc_outb(ahc, DSCOMMAND1, dscommand1);
+               }
+               break;
+       }
+       case MKMSG_FAILED:
+       {
+               u_int scbindex;
+
+               printf("%s:%c:%d:%d: Attempt to issue message failed\n",
+                      ahc_name(ahc), devinfo.channel, devinfo.target,
+                      devinfo.lun);
+               scbindex = ahc_inb(ahc, SCB_TAG);
+               scb = ahc_lookup_scb(ahc, scbindex);
+               if (scb != NULL
+                && (scb->flags & SCB_RECOVERY_SCB) != 0)
+                       /*
+                        * Ensure that we didn't put a second instance of this
+                        * SCB into the QINFIFO.
+                        */
+                       ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+                                          SCB_GET_CHANNEL(ahc, scb),
+                                          SCB_GET_LUN(scb), scb->hscb->tag,
+                                          ROLE_INITIATOR, /*status*/0,
+                                          SEARCH_REMOVE);
+               break;
+       }
+       case NO_FREE_SCB:
+       {
+               printf("%s: No free or disconnected SCBs\n", ahc_name(ahc));
+               ahc_dump_card_state(ahc);
+               panic("for safety");
+               break;
+       }
+       case SCB_MISMATCH:
+       {
+               u_int scbptr;
+
+               scbptr = ahc_inb(ahc, SCBPTR);
+               printf("Bogus TAG after DMA.  SCBPTR %d, tag %d, our tag %d\n",
+                      scbptr, ahc_inb(ahc, ARG_1),
+                      ahc->scb_data->hscbs[scbptr].tag);
+               ahc_dump_card_state(ahc);
+               panic("for saftey");
+               break;
+       }
+       case OUT_OF_RANGE:
+       {
+               printf("%s: BTT calculation out of range\n", ahc_name(ahc));
+               printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+                      "ARG_1 == 0x%x ACCUM = 0x%x\n",
+                      ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
+                      ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
+               printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+                      "SINDEX == 0x%x\n, A == 0x%x\n",
+                      ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
+                      ahc_index_busy_tcl(ahc,
+                           BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
+                                     ahc_inb(ahc, SAVED_LUN))),
+                      ahc_inb(ahc, SINDEX),
+                      ahc_inb(ahc, ACCUM));
+               printf("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+                      "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
+                      ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
+                      ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
+                      ahc_inb(ahc, SCB_CONTROL));
+               printf("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
+                      ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
+               ahc_dump_card_state(ahc);
+               panic("for safety");
+               break;
+       }
+       default:
+               printf("ahc_intr: seqint, "
+                      "intstat == 0x%x, scsisigi = 0x%x\n",
+                      intstat, ahc_inb(ahc, SCSISIGI));
+               break;
+       }
+unpause:
+       /*
+        *  The sequencer is paused immediately on
+        *  a SEQINT, so we should restart it when
+        *  we're done.
+        */
+       ahc_unpause(ahc);
+}
+
+void
+ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
+{
+       u_int   scb_index;
+       u_int   status0;
+       u_int   status;
+       struct  scb *scb;
+       char    cur_channel;
+       char    intr_channel;
+
+       /* Make sure the sequencer is in a safe location. */
+       ahc_clear_critical_section(ahc);
+
+       if ((ahc->features & AHC_TWIN) != 0
+        && ((ahc_inb(ahc, SBLKCTL) & SELBUSB) != 0))
+               cur_channel = 'B';
+       else
+               cur_channel = 'A';
+       intr_channel = cur_channel;
+
+       if ((ahc->features & AHC_ULTRA2) != 0)
+               status0 = ahc_inb(ahc, SSTAT0) & IOERR;
+       else
+               status0 = 0;
+       status = ahc_inb(ahc, SSTAT1) & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+       if (status == 0 && status0 == 0) {
+               if ((ahc->features & AHC_TWIN) != 0) {
+                       /* Try the other channel */
+                       ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+                       status = ahc_inb(ahc, SSTAT1)
+                              & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+                       intr_channel = (cur_channel == 'A') ? 'B' : 'A';
+               }
+               if (status == 0) {
+                       printf("%s: Spurious SCSI interrupt\n", ahc_name(ahc));
+                       ahc_outb(ahc, CLRINT, CLRSCSIINT);
+                       ahc_unpause(ahc);
+                       return;
+               }
+       }
+
+       scb_index = ahc_inb(ahc, SCB_TAG);
+       scb = ahc_lookup_scb(ahc, scb_index);
+       if (scb != NULL
+        && (ahc_inb(ahc, SEQ_FLAGS) & IDENTIFY_SEEN) == 0)
+               scb = NULL;
+
+       if ((ahc->features & AHC_ULTRA2) != 0
+        && (status0 & IOERR) != 0) {
+               int now_lvd;
+
+               now_lvd = ahc_inb(ahc, SBLKCTL) & ENAB40;
+               printf("%s: Transceiver State Has Changed to %s mode\n",
+                      ahc_name(ahc), now_lvd ? "LVD" : "SE");
+               ahc_outb(ahc, CLRSINT0, CLRIOERR);
+               /*
+                * When transitioning to SE mode, the reset line
+                * glitches, triggering an arbitration bug in some
+                * Ultra2 controllers.  This bug is cleared when we
+                * assert the reset line.  Since a reset glitch has
+                * already occurred with this transition and a
+                * transceiver state change is handled just like
+                * a bus reset anyway, asserting the reset line
+                * ourselves is safe.
+                */
+               ahc_reset_channel(ahc, intr_channel,
+                                /*Initiate Reset*/now_lvd == 0);
+       } else if ((status & SCSIRSTI) != 0) {
+               printf("%s: Someone reset channel %c\n",
+                       ahc_name(ahc), intr_channel);
+               if (intr_channel != cur_channel)
+                       ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+               ahc_reset_channel(ahc, intr_channel, /*Initiate Reset*/FALSE);
+       } else if ((status & SCSIPERR) != 0) {
+               /*
+                * Determine the bus phase and queue an appropriate message.
+                * SCSIPERR is latched true as soon as a parity error
+                * occurs.  If the sequencer acked the transfer that
+                * caused the parity error and the currently presented
+                * transfer on the bus has correct parity, SCSIPERR will
+                * be cleared by CLRSCSIPERR.  Use this to determine if
+                * we should look at the last phase the sequencer recorded,
+                * or the current phase presented on the bus.
+                */
+               u_int mesg_out;
+               u_int curphase;
+               u_int errorphase;
+               u_int lastphase;
+               u_int scsirate;
+               u_int i;
+               u_int sstat2;
+
+               lastphase = ahc_inb(ahc, LASTPHASE);
+               curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+               sstat2 = ahc_inb(ahc, SSTAT2);
+               ahc_outb(ahc, CLRSINT1, CLRSCSIPERR);
+               /*
+                * For all phases save DATA, the sequencer won't
+                * automatically ack a byte that has a parity error
+                * in it.  So the only way that the current phase
+                * could be 'data-in' is if the parity error is for
+                * an already acked byte in the data phase.  During
+                * synchronous data-in transfers, we may actually
+                * ack bytes before latching the current phase in
+                * LASTPHASE, leading to the discrepancy between
+                * curphase and lastphase.
+                */
+               if ((ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0
+                || curphase == P_DATAIN || curphase == P_DATAIN_DT)
+                       errorphase = curphase;
+               else
+                       errorphase = lastphase;
+
+               for (i = 0; i < num_phases; i++) {
+                       if (errorphase == ahc_phase_table[i].phase)
+                               break;
+               }
+               mesg_out = ahc_phase_table[i].mesg_out;
+               if (scb != NULL)
+                       ahc_print_path(ahc, scb);
+               else
+                       printf("%s:%c:%d: ", ahc_name(ahc), intr_channel,
+                              SCSIID_TARGET(ahc, ahc_inb(ahc, SAVED_SCSIID)));
+               scsirate = ahc_inb(ahc, SCSIRATE);
+               printf("parity error detected %s. "
+                      "SEQADDR(0x%x) SCSIRATE(0x%x)\n",
+                      ahc_phase_table[i].phasemsg,
+                      ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8),
+                      scsirate);
+
+               if ((ahc->features & AHC_DT) != 0) {
+
+                       if ((sstat2 & CRCVALERR) != 0)
+                               printf("\tCRC Value Mismatch\n");
+                       if ((sstat2 & CRCENDERR) != 0)
+                               printf("\tNo terminal CRC packet recevied\n");
+                       if ((sstat2 & CRCREQERR) != 0)
+                               printf("\tIllegal CRC packet request\n");
+                       if ((sstat2 & DUAL_EDGE_ERR) != 0)
+                               printf("\tUnexpected %sDT Data Phase\n",
+                                      (scsirate & SINGLE_EDGE) ? "" : "non-");
+               }
+
+               /*
+                * We've set the hardware to assert ATN if we   
+                * get a parity error on "in" phases, so all we  
+                * need to do is stuff the message buffer with
+                * the appropriate message.  "In" phases have set
+                * mesg_out to something other than MSG_NOP.
+                */
+               if (mesg_out != MSG_NOOP) {
+                       if (ahc->msg_type != MSG_TYPE_NONE)
+                               ahc->send_msg_perror = TRUE;
+                       else
+                               ahc_outb(ahc, MSG_OUT, mesg_out);
+               }
+               /*
+                * Force a renegotiation with this target just in
+                * case we are out of sync for some external reason
+                * unknown (or unreported) by the target.
+                */
+               ahc_force_renegotiation(ahc);
+               ahc_outb(ahc, CLRINT, CLRSCSIINT);
+               ahc_unpause(ahc);
+       } else if ((status & SELTO) != 0) {
+               u_int   scbptr;
+
+               /* Stop the selection */
+               ahc_outb(ahc, SCSISEQ, 0);
+
+               /* No more pending messages */
+               ahc_clear_msg_state(ahc);
+
+               /* Clear interrupt state */
+               ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
+               ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
+
+               /*
+                * Although the driver does not care about the
+                * 'Selection in Progress' status bit, the busy
+                * LED does.  SELINGO is only cleared by a sucessfull
+                * selection, so we must manually clear it to insure
+                * the LED turns off just incase no future successful
+                * selections occur (e.g. no devices on the bus).
+                */
+               ahc_outb(ahc, CLRSINT0, CLRSELINGO);
+
+               scbptr = ahc_inb(ahc, WAITING_SCBH);
+               ahc_outb(ahc, SCBPTR, scbptr);
+               scb_index = ahc_inb(ahc, SCB_TAG);
+
+               scb = ahc_lookup_scb(ahc, scb_index);
+               if (scb == NULL) {
+                       printf("%s: ahc_intr - referenced scb not "
+                              "valid during SELTO scb(%d, %d)\n",
+                              ahc_name(ahc), scbptr, scb_index);
+               } else {
+                       ahc_set_transaction_status(scb, CAM_SEL_TIMEOUT);
+                       ahc_freeze_devq(ahc, scb);
+               }
+               ahc_outb(ahc, CLRINT, CLRSCSIINT);
+               /*
+                * Force a renegotiation with this target just in
+                * case the cable was pulled and will later be
+                * re-attached.  The target may forget its negotiation
+                * settings with us should it attempt to reselect
+                * during the interruption.  The target will not issue
+                * a unit attention in this case, so we must always
+                * renegotiate.
+                */
+               ahc_force_renegotiation(ahc);
+               ahc_restart(ahc);
+       } else if ((status & BUSFREE) != 0
+               && (ahc_inb(ahc, SIMODE1) & ENBUSFREE) != 0) {
+               u_int lastphase;
+               u_int saved_scsiid;
+               u_int saved_lun;
+               u_int target;
+               u_int initiator_role_id;
+               char channel;
+               int printerror;
+
+               /*
+                * Clear our selection hardware as soon as possible.
+                * We may have an entry in the waiting Q for this target,
+                * that is affected by this busfree and we don't want to
+                * go about selecting the target while we handle the event.
+                */
+               ahc_outb(ahc, SCSISEQ,
+                        ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+
+               /*
+                * Disable busfree interrupts and clear the busfree
+                * interrupt status.  We do this here so that several
+                * bus transactions occur prior to clearing the SCSIINT
+                * latch.  It can take a bit for the clearing to take effect.
+                */
+               ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
+               ahc_outb(ahc, CLRSINT1, CLRBUSFREE|CLRSCSIPERR);
+
+               /*
+                * Look at what phase we were last in.
+                * If its message out, chances are pretty good
+                * that the busfree was in response to one of
+                * our abort requests.
+                */
+               lastphase = ahc_inb(ahc, LASTPHASE);
+               saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+               saved_lun = ahc_inb(ahc, SAVED_LUN);
+               target = SCSIID_TARGET(ahc, saved_scsiid);
+               initiator_role_id = SCSIID_OUR_ID(saved_scsiid);
+               channel = SCSIID_CHANNEL(ahc, saved_scsiid);
+               printerror = 1;
+
+               if (lastphase == P_MESGOUT) {
+                       struct ahc_devinfo devinfo;
+                       u_int tag;
+
+                       ahc_fetch_devinfo(ahc, &devinfo);
+                       tag = SCB_LIST_NULL;
+                       if (ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT_TAG, TRUE)
+                        || ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT, TRUE)) {
+                               if (ahc->msgout_buf[ahc->msgout_index - 1]
+                                == MSG_ABORT_TAG)
+                                       tag = scb->hscb->tag;
+                               ahc_print_path(ahc, scb);
+                               printf("SCB %d - Abort%s Completed.\n",
+                                      scb->hscb->tag, tag == SCB_LIST_NULL ?
+                                      "" : " Tag");
+                               ahc_abort_scbs(ahc, target, channel,
+                                              saved_lun, tag,
+                                              ROLE_INITIATOR,
+                                              CAM_REQ_ABORTED);
+                               printerror = 0;
+                       } else if (ahc_sent_msg(ahc, AHCMSG_1B,
+                                               MSG_BUS_DEV_RESET, TRUE)) {
+#ifdef __FreeBSD__
+                               /*
+                                * Don't mark the user's request for this BDR
+                                * as completing with CAM_BDR_SENT.  CAM3
+                                * specifies CAM_REQ_CMP.
+                                */
+                               if (scb != NULL
+                                && scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
+                                && ahc_match_scb(ahc, scb, target, channel,
+                                                 CAM_LUN_WILDCARD,
+                                                 SCB_LIST_NULL,
+                                                 ROLE_INITIATOR)) {
+                                       ahc_set_transaction_status(scb, CAM_REQ_CMP);
+                               }
+#endif
+                               ahc_compile_devinfo(&devinfo,
+                                                   initiator_role_id,
+                                                   target,
+                                                   CAM_LUN_WILDCARD,
+                                                   channel,
+                                                   ROLE_INITIATOR);
+                               ahc_handle_devreset(ahc, &devinfo,
+                                                   CAM_BDR_SENT,
+                                                   "Bus Device Reset",
+                                                   /*verbose_level*/0);
+                               printerror = 0;
+                       } else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+                                               MSG_EXT_PPR, FALSE)) {
+                               struct ahc_initiator_tinfo *tinfo;
+                               struct ahc_tmode_tstate *tstate;
+
+                               /*
+                                * PPR Rejected.  Try non-ppr negotiation
+                                * and retry command.
+                                */
+                               tinfo = ahc_fetch_transinfo(ahc,
+                                                           devinfo.channel,
+                                                           devinfo.our_scsiid,
+                                                           devinfo.target,
+                                                           &tstate);
+                               tinfo->curr.transport_version = 2;
+                               tinfo->goal.transport_version = 2;
+                               tinfo->goal.ppr_options = 0;
+                               ahc_qinfifo_requeue_tail(ahc, scb);
+                               printerror = 0;
+                       } else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+                                               MSG_EXT_WDTR, FALSE)
+                               || ahc_sent_msg(ahc, AHCMSG_EXT,
+                                               MSG_EXT_SDTR, FALSE)) {
+                               /*
+                                * Negotiation Rejected.  Go-async and
+                                * retry command.
+                                */
+                               ahc_set_width(ahc, &devinfo,
+                                             MSG_EXT_WDTR_BUS_8_BIT,
+                                             AHC_TRANS_CUR|AHC_TRANS_GOAL,
+                                             /*paused*/TRUE);
+                               ahc_set_syncrate(ahc, &devinfo,
+                                               /*syncrate*/NULL,
+                                               /*period*/0, /*offset*/0,
+                                               /*ppr_options*/0,
+                                               AHC_TRANS_CUR|AHC_TRANS_GOAL,
+                                               /*paused*/TRUE);
+                               ahc_qinfifo_requeue_tail(ahc, scb);
+                               printerror = 0;
+                       }
+               }
+               if (printerror != 0) {
+                       u_int i;
+
+                       if (scb != NULL) {
+                               u_int tag;
+
+                               if ((scb->hscb->control & TAG_ENB) != 0)
+                                       tag = scb->hscb->tag;
+                               else
+                                       tag = SCB_LIST_NULL;
+                               ahc_print_path(ahc, scb);
+                               ahc_abort_scbs(ahc, target, channel,
+                                              SCB_GET_LUN(scb), tag,
+                                              ROLE_INITIATOR,
+                                              CAM_UNEXP_BUSFREE);
+                       } else {
+                               /*
+                                * We had not fully identified this connection,
+                                * so we cannot abort anything.
+                                */
+                               printf("%s: ", ahc_name(ahc));
+                       }
+                       for (i = 0; i < num_phases; i++) {
+                               if (lastphase == ahc_phase_table[i].phase)
+                                       break;
+                       }
+                       /*
+                        * Renegotiate with this device at the
+                        * next oportunity just in case this busfree
+                        * is due to a negotiation mismatch with the
+                        * device.
+                        */
+                       ahc_force_renegotiation(ahc);
+                       printf("Unexpected busfree %s\n"
+                              "SEQADDR == 0x%x\n",
+                              ahc_phase_table[i].phasemsg,
+                              ahc_inb(ahc, SEQADDR0)
+                               | (ahc_inb(ahc, SEQADDR1) << 8));
+               }
+               ahc_outb(ahc, CLRINT, CLRSCSIINT);
+               ahc_restart(ahc);
+       } else {
+               printf("%s: Missing case in ahc_handle_scsiint. status = %x\n",
+                      ahc_name(ahc), status);
+               ahc_outb(ahc, CLRINT, CLRSCSIINT);
+       }
+}
+
+/*
+ * Force renegotiation to occur the next time we initiate
+ * a command to the current device.
+ */
+static void
+ahc_force_renegotiation(struct ahc_softc *ahc)
+{
+       struct  ahc_devinfo devinfo;
+       struct  ahc_initiator_tinfo *targ_info;
+       struct  ahc_tmode_tstate *tstate;
+
+       ahc_fetch_devinfo(ahc, &devinfo);
+       targ_info = ahc_fetch_transinfo(ahc,
+                                       devinfo.channel,
+                                       devinfo.our_scsiid,
+                                       devinfo.target,
+                                       &tstate);
+       ahc_update_neg_request(ahc, &devinfo, tstate,
+                              targ_info, /*force*/TRUE);
+}
+
+#define AHC_MAX_STEPS 2000
+void
+ahc_clear_critical_section(struct ahc_softc *ahc)
+{
+       int     stepping;
+       int     steps;
+       u_int   simode0;
+       u_int   simode1;
+
+       if (ahc->num_critical_sections == 0)
+               return;
+
+       stepping = FALSE;
+       steps = 0;
+       simode0 = 0;
+       simode1 = 0;
+       for (;;) {
+               struct  cs *cs;
+               u_int   seqaddr;
+               u_int   i;
+
+               seqaddr = ahc_inb(ahc, SEQADDR0)
+                       | (ahc_inb(ahc, SEQADDR1) << 8);
+
+               /*
+                * Seqaddr represents the next instruction to execute, 
+                * so we are really executing the instruction just
+                * before it.
+                */
+               if (seqaddr != 0)
+                       seqaddr -= 1;
+               cs = ahc->critical_sections;
+               for (i = 0; i < ahc->num_critical_sections; i++, cs++) {
+                       
+                       if (cs->begin < seqaddr && cs->end >= seqaddr)
+                               break;
+               }
+
+               if (i == ahc->num_critical_sections)
+                       break;
+
+               if (steps > AHC_MAX_STEPS) {
+                       printf("%s: Infinite loop in critical section\n",
+                              ahc_name(ahc));
+                       ahc_dump_card_state(ahc);
+                       panic("critical section loop");
+               }
+
+               steps++;
+               if (stepping == FALSE) {
+
+                       /*
+                        * Disable all interrupt sources so that the
+                        * sequencer will not be stuck by a pausing
+                        * interrupt condition while we attempt to
+                        * leave a critical section.
+                        */
+                       simode0 = ahc_inb(ahc, SIMODE0);
+                       ahc_outb(ahc, SIMODE0, 0);
+                       simode1 = ahc_inb(ahc, SIMODE1);
+                       ahc_outb(ahc, SIMODE1, 0);
+                       ahc_outb(ahc, CLRINT, CLRSCSIINT);
+                       ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) | STEP);
+                       stepping = TRUE;
+               }
+               ahc_outb(ahc, HCNTRL, ahc->unpause);
+               while (!ahc_is_paused(ahc))
+                       ahc_delay(200);
+       }
+       if (stepping) {
+               ahc_outb(ahc, SIMODE0, simode0);
+               ahc_outb(ahc, SIMODE1, simode1);
+               ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) & ~STEP);
+       }
+}
+
+/*
+ * Clear any pending interrupt status.
+ */
+void
+ahc_clear_intstat(struct ahc_softc *ahc)
+{
+       /* Clear any interrupt conditions this may have caused */
+       ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
+                               |CLRBUSFREE|CLRSCSIPERR|CLRPHASECHG|
+                               CLRREQINIT);
+       ahc_flush_device_writes(ahc);
+       ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO);
+       ahc_flush_device_writes(ahc);
+       ahc_outb(ahc, CLRINT, CLRSCSIINT);
+       ahc_flush_device_writes(ahc);
+}
+
+/**************************** Debugging Routines ******************************/
+#ifdef AHC_DEBUG
+int ahc_debug = AHC_DEBUG;
+#endif
+
+void
+ahc_print_scb(struct scb *scb)
+{
+       int i;
+
+       struct hardware_scb *hscb = scb->hscb;
+
+       printf("scb:%p control:0x%x scsiid:0x%x lun:%d cdb_len:%d\n",
+              (void *)scb,
+              hscb->control,
+              hscb->scsiid,
+              hscb->lun,
+              hscb->cdb_len);
+       printf("Shared Data: ");
+       for (i = 0; i < sizeof(hscb->shared_data.cdb); i++)
+               printf("%#02x", hscb->shared_data.cdb[i]);
+       printf("        dataptr:%#x datacnt:%#x sgptr:%#x tag:%#x\n",
+               ahc_le32toh(hscb->dataptr),
+               ahc_le32toh(hscb->datacnt),
+               ahc_le32toh(hscb->sgptr),
+               hscb->tag);
+       if (scb->sg_count > 0) {
+               for (i = 0; i < scb->sg_count; i++) {
+                       printf("sg[%d] - Addr 0x%x%x : Length %d\n",
+                              i,
+                              (ahc_le32toh(scb->sg_list[i].len) >> 24
+                               & SG_HIGH_ADDR_BITS),
+                              ahc_le32toh(scb->sg_list[i].addr),
+                              ahc_le32toh(scb->sg_list[i].len));
+               }
+       }
+}
+
+/************************* Transfer Negotiation *******************************/
+/*
+ * Allocate per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static struct ahc_tmode_tstate *
+ahc_alloc_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel)
+{
+       struct ahc_tmode_tstate *master_tstate;
+       struct ahc_tmode_tstate *tstate;
+       int i;
+
+       master_tstate = ahc->enabled_targets[ahc->our_id];
+       if (channel == 'B') {
+               scsi_id += 8;
+               master_tstate = ahc->enabled_targets[ahc->our_id_b + 8];
+       }
+       if (ahc->enabled_targets[scsi_id] != NULL
+        && ahc->enabled_targets[scsi_id] != master_tstate)
+               panic("%s: ahc_alloc_tstate - Target already allocated",
+                     ahc_name(ahc));
+       tstate = malloc(sizeof(*tstate), M_DEVBUF, M_NOWAIT);
+       if (tstate == NULL)
+               return (NULL);
+
+       /*
+        * If we have allocated a master tstate, copy user settings from
+        * the master tstate (taken from SRAM or the EEPROM) for this
+        * channel, but reset our current and goal settings to async/narrow
+        * until an initiator talks to us.
+        */
+       if (master_tstate != NULL) {
+               memcpy(tstate, master_tstate, sizeof(*tstate));
+               memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
+               tstate->ultraenb = 0;
+               for (i = 0; i < AHC_NUM_TARGETS; i++) {
+                       memset(&tstate->transinfo[i].curr, 0,
+                             sizeof(tstate->transinfo[i].curr));
+                       memset(&tstate->transinfo[i].goal, 0,
+                             sizeof(tstate->transinfo[i].goal));
+               }
+       } else
+               memset(tstate, 0, sizeof(*tstate));
+       ahc->enabled_targets[scsi_id] = tstate;
+       return (tstate);
+}
+
+#ifdef AHC_TARGET_MODE
+/*
+ * Free per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static void
+ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
+{
+       struct ahc_tmode_tstate *tstate;
+
+       /*
+        * Don't clean up our "master" tstate.
+        * It has our default user settings.
+        */
+       if (((channel == 'B' && scsi_id == ahc->our_id_b)
+         || (channel == 'A' && scsi_id == ahc->our_id))
+        && force == FALSE)
+               return;
+
+       if (channel == 'B')
+               scsi_id += 8;
+       tstate = ahc->enabled_targets[scsi_id];
+       if (tstate != NULL)
+               free(tstate, M_DEVBUF);
+       ahc->enabled_targets[scsi_id] = NULL;
+}
+#endif
+
+/*
+ * Called when we have an active connection to a target on the bus,
+ * this function finds the nearest syncrate to the input period limited
+ * by the capabilities of the bus connectivity of and sync settings for
+ * the target.
+ */
+struct ahc_syncrate *
+ahc_devlimited_syncrate(struct ahc_softc *ahc,
+                       struct ahc_initiator_tinfo *tinfo,
+                       u_int *period, u_int *ppr_options, role_t role)
+{
+       struct  ahc_transinfo *transinfo;
+       u_int   maxsync;
+
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0
+                && (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) {
+                       maxsync = AHC_SYNCRATE_DT;
+               } else {
+                       maxsync = AHC_SYNCRATE_ULTRA;
+                       /* Can't do DT on an SE bus */
+                       *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+               }
+       } else if ((ahc->features & AHC_ULTRA) != 0) {
+               maxsync = AHC_SYNCRATE_ULTRA;
+       } else {
+               maxsync = AHC_SYNCRATE_FAST;
+       }
+       /*
+        * Never allow a value higher than our current goal
+        * period otherwise we may allow a target initiated
+        * negotiation to go above the limit as set by the
+        * user.  In the case of an initiator initiated
+        * sync negotiation, we limit based on the user
+        * setting.  This allows the system to still accept
+        * incoming negotiations even if target initiated
+        * negotiation is not performed.
+        */
+       if (role == ROLE_TARGET)
+               transinfo = &tinfo->user;
+       else 
+               transinfo = &tinfo->goal;
+       *ppr_options &= transinfo->ppr_options;
+       if (transinfo->period == 0) {
+               *period = 0;
+               *ppr_options = 0;
+               return (NULL);
+       }
+       *period = MAX(*period, transinfo->period);
+       return (ahc_find_syncrate(ahc, period, ppr_options, maxsync));
+}
+
+/*
+ * Look up the valid period to SCSIRATE conversion in our table.
+ * Return the period and offset that should be sent to the target
+ * if this was the beginning of an SDTR.
+ */
+struct ahc_syncrate *
+ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
+                 u_int *ppr_options, u_int maxsync)
+{
+       struct ahc_syncrate *syncrate;
+
+       if ((ahc->features & AHC_DT) == 0)
+               *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+
+       /* Skip all DT only entries if DT is not available */
+       if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+        && maxsync < AHC_SYNCRATE_ULTRA2)
+               maxsync = AHC_SYNCRATE_ULTRA2;
+       
+       for (syncrate = &ahc_syncrates[maxsync];
+            syncrate->rate != NULL;
+            syncrate++) {
+
+               /*
+                * The Ultra2 table doesn't go as low
+                * as for the Fast/Ultra cards.
+                */
+               if ((ahc->features & AHC_ULTRA2) != 0
+                && (syncrate->sxfr_u2 == 0))
+                       break;
+
+               if (*period <= syncrate->period) {
+                       /*
+                        * When responding to a target that requests
+                        * sync, the requested rate may fall between
+                        * two rates that we can output, but still be
+                        * a rate that we can receive.  Because of this,
+                        * we want to respond to the target with
+                        * the same rate that it sent to us even
+                        * if the period we use to send data to it
+                        * is lower.  Only lower the response period
+                        * if we must.
+                        */
+                       if (syncrate == &ahc_syncrates[maxsync])
+                               *period = syncrate->period;
+
+                       /*
+                        * At some speeds, we only support
+                        * ST transfers.
+                        */
+                       if ((syncrate->sxfr_u2 & ST_SXFR) != 0)
+                               *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+                       break;
+               }
+       }
+
+       if ((*period == 0)
+        || (syncrate->rate == NULL)
+        || ((ahc->features & AHC_ULTRA2) != 0
+         && (syncrate->sxfr_u2 == 0))) {
+               /* Use asynchronous transfers. */
+               *period = 0;
+               syncrate = NULL;
+               *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+       }
+       return (syncrate);
+}
+
+/*
+ * Convert from an entry in our syncrate table to the SCSI equivalent
+ * sync "period" factor.
+ */
+u_int
+ahc_find_period(struct ahc_softc *ahc, u_int scsirate, u_int maxsync)
+{
+       struct ahc_syncrate *syncrate;
+
+       if ((ahc->features & AHC_ULTRA2) != 0)
+               scsirate &= SXFR_ULTRA2;
+       else
+               scsirate &= SXFR;
+
+       syncrate = &ahc_syncrates[maxsync];
+       while (syncrate->rate != NULL) {
+
+               if ((ahc->features & AHC_ULTRA2) != 0) {
+                       if (syncrate->sxfr_u2 == 0)
+                               break;
+                       else if (scsirate == (syncrate->sxfr_u2 & SXFR_ULTRA2))
+                               return (syncrate->period);
+               } else if (scsirate == (syncrate->sxfr & SXFR)) {
+                               return (syncrate->period);
+               }
+               syncrate++;
+       }
+       return (0); /* async */
+}
+
+/*
+ * Truncate the given synchronous offset to a value the
+ * current adapter type and syncrate are capable of.
+ */
+void
+ahc_validate_offset(struct ahc_softc *ahc,
+                   struct ahc_initiator_tinfo *tinfo,
+                   struct ahc_syncrate *syncrate,
+                   u_int *offset, int wide, role_t role)
+{
+       u_int maxoffset;
+
+       /* Limit offset to what we can do */
+       if (syncrate == NULL) {
+               maxoffset = 0;
+       } else if ((ahc->features & AHC_ULTRA2) != 0) {
+               maxoffset = MAX_OFFSET_ULTRA2;
+       } else {
+               if (wide)
+                       maxoffset = MAX_OFFSET_16BIT;
+               else
+                       maxoffset = MAX_OFFSET_8BIT;
+       }
+       *offset = MIN(*offset, maxoffset);
+       if (tinfo != NULL) {
+               if (role == ROLE_TARGET)
+                       *offset = MIN(*offset, tinfo->user.offset);
+               else
+                       *offset = MIN(*offset, tinfo->goal.offset);
+       }
+}
+
+/*
+ * Truncate the given transfer width parameter to a value the
+ * current adapter type is capable of.
+ */
+void
+ahc_validate_width(struct ahc_softc *ahc, struct ahc_initiator_tinfo *tinfo,
+                  u_int *bus_width, role_t role)
+{
+       switch (*bus_width) {
+       default:
+               if (ahc->features & AHC_WIDE) {
+                       /* Respond Wide */
+                       *bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+                       break;
+               }
+               /* FALLTHROUGH */
+       case MSG_EXT_WDTR_BUS_8_BIT:
+               *bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+               break;
+       }
+       if (tinfo != NULL) {
+               if (role == ROLE_TARGET)
+                       *bus_width = MIN(tinfo->user.width, *bus_width);
+               else
+                       *bus_width = MIN(tinfo->goal.width, *bus_width);
+       }
+}
+
+/*
+ * Update the bitmask of targets for which the controller should
+ * negotiate with at the next convenient oportunity.  This currently
+ * means the next time we send the initial identify messages for
+ * a new transaction.
+ */
+int
+ahc_update_neg_request(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+                      struct ahc_tmode_tstate *tstate,
+                      struct ahc_initiator_tinfo *tinfo, int force)
+{
+       u_int auto_negotiate_orig;
+
+       auto_negotiate_orig = tstate->auto_negotiate;
+       if (tinfo->curr.period != tinfo->goal.period
+        || tinfo->curr.width != tinfo->goal.width
+        || tinfo->curr.offset != tinfo->goal.offset
+        || tinfo->curr.ppr_options != tinfo->goal.ppr_options
+        || (force
+         && (tinfo->goal.period != 0
+          || tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
+          || tinfo->goal.ppr_options != 0)))
+               tstate->auto_negotiate |= devinfo->target_mask;
+       else
+               tstate->auto_negotiate &= ~devinfo->target_mask;
+
+       return (auto_negotiate_orig != tstate->auto_negotiate);
+}
+
+/*
+ * Update the user/goal/curr tables of synchronous negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller.  In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+                struct ahc_syncrate *syncrate, u_int period,
+                u_int offset, u_int ppr_options, u_int type, int paused)
+{
+       struct  ahc_initiator_tinfo *tinfo;
+       struct  ahc_tmode_tstate *tstate;
+       u_int   old_period;
+       u_int   old_offset;
+       u_int   old_ppr;
+       int     active;
+       int     update_needed;
+
+       active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
+       update_needed = 0;
+
+       if (syncrate == NULL) {
+               period = 0;
+               offset = 0;
+       }
+
+       tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+                                   devinfo->target, &tstate);
+
+       if ((type & AHC_TRANS_USER) != 0) {
+               tinfo->user.period = period;
+               tinfo->user.offset = offset;
+               tinfo->user.ppr_options = ppr_options;
+       }
+
+       if ((type & AHC_TRANS_GOAL) != 0) {
+               tinfo->goal.period = period;
+               tinfo->goal.offset = offset;
+               tinfo->goal.ppr_options = ppr_options;
+       }
+
+       old_period = tinfo->curr.period;
+       old_offset = tinfo->curr.offset;
+       old_ppr    = tinfo->curr.ppr_options;
+
+       if ((type & AHC_TRANS_CUR) != 0
+        && (old_period != period
+         || old_offset != offset
+         || old_ppr != ppr_options)) {
+               u_int   scsirate;
+
+               update_needed++;
+               scsirate = tinfo->scsirate;
+               if ((ahc->features & AHC_ULTRA2) != 0) {
+
+                       scsirate &= ~(SXFR_ULTRA2|SINGLE_EDGE|ENABLE_CRC);
+                       if (syncrate != NULL) {
+                               scsirate |= syncrate->sxfr_u2;
+                               if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0)
+                                       scsirate |= ENABLE_CRC;
+                               else
+                                       scsirate |= SINGLE_EDGE;
+                       }
+               } else {
+
+                       scsirate &= ~(SXFR|SOFS);
+                       /*
+                        * Ensure Ultra mode is set properly for
+                        * this target.
+                        */
+                       tstate->ultraenb &= ~devinfo->target_mask;
+                       if (syncrate != NULL) {
+                               if (syncrate->sxfr & ULTRA_SXFR) {
+                                       tstate->ultraenb |=
+                                               devinfo->target_mask;
+                               }
+                               scsirate |= syncrate->sxfr & SXFR;
+                               scsirate |= offset & SOFS;
+                       }
+                       if (active) {
+                               u_int sxfrctl0;
+
+                               sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+                               sxfrctl0 &= ~FAST20;
+                               if (tstate->ultraenb & devinfo->target_mask)
+                                       sxfrctl0 |= FAST20;
+                               ahc_outb(ahc, SXFRCTL0, sxfrctl0);
+                       }
+               }
+               if (active) {
+                       ahc_outb(ahc, SCSIRATE, scsirate);
+                       if ((ahc->features & AHC_ULTRA2) != 0)
+                               ahc_outb(ahc, SCSIOFFSET, offset);
+               }
+
+               tinfo->scsirate = scsirate;
+               tinfo->curr.period = period;
+               tinfo->curr.offset = offset;
+               tinfo->curr.ppr_options = ppr_options;
+
+               ahc_send_async(ahc, devinfo->channel, devinfo->target,
+                              CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+               if (bootverbose) {
+                       if (offset != 0) {
+                               printf("%s: target %d synchronous at %sMHz%s, "
+                                      "offset = 0x%x\n", ahc_name(ahc),
+                                      devinfo->target, syncrate->rate,
+                                      (ppr_options & MSG_EXT_PPR_DT_REQ)
+                                      ? " DT" : "", offset);
+                       } else {
+                               printf("%s: target %d using "
+                                      "asynchronous transfers\n",
+                                      ahc_name(ahc), devinfo->target);
+                       }
+               }
+       }
+
+       update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
+                                               tinfo, /*force*/FALSE);
+
+       if (update_needed)
+               ahc_update_pending_scbs(ahc);
+}
+
+/*
+ * Update the user/goal/curr tables of wide negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller.  In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+             u_int width, u_int type, int paused)
+{
+       struct  ahc_initiator_tinfo *tinfo;
+       struct  ahc_tmode_tstate *tstate;
+       u_int   oldwidth;
+       int     active;
+       int     update_needed;
+
+       active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
+       update_needed = 0;
+       tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+                                   devinfo->target, &tstate);
+
+       if ((type & AHC_TRANS_USER) != 0)
+               tinfo->user.width = width;
+
+       if ((type & AHC_TRANS_GOAL) != 0)
+               tinfo->goal.width = width;
+
+       oldwidth = tinfo->curr.width;
+       if ((type & AHC_TRANS_CUR) != 0 && oldwidth != width) {
+               u_int   scsirate;
+
+               update_needed++;
+               scsirate =  tinfo->scsirate;
+               scsirate &= ~WIDEXFER;
+               if (width == MSG_EXT_WDTR_BUS_16_BIT)
+                       scsirate |= WIDEXFER;
+
+               tinfo->scsirate = scsirate;
+
+               if (active)
+                       ahc_outb(ahc, SCSIRATE, scsirate);
+
+               tinfo->curr.width = width;
+
+               ahc_send_async(ahc, devinfo->channel, devinfo->target,
+                              CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+               if (bootverbose) {
+                       printf("%s: target %d using %dbit transfers\n",
+                              ahc_name(ahc), devinfo->target,
+                              8 * (0x01 << width));
+               }
+       }
+
+       update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
+                                               tinfo, /*force*/FALSE);
+       if (update_needed)
+               ahc_update_pending_scbs(ahc);
+}
+
+/*
+ * Update the current state of tagged queuing for a given target.
+ */
+void
+ahc_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+            ahc_queue_alg alg)
+{
+       ahc_platform_set_tags(ahc, devinfo, alg);
+       ahc_send_async(ahc, devinfo->channel, devinfo->target,
+                      devinfo->lun, AC_TRANSFER_NEG, &alg);
+}
+
+/*
+ * When the transfer settings for a connection change, update any
+ * in-transit SCBs to contain the new data so the hardware will
+ * be set correctly during future (re)selections.
+ */
+static void
+ahc_update_pending_scbs(struct ahc_softc *ahc)
+{
+       struct  scb *pending_scb;
+       int     pending_scb_count;
+       int     i;
+       int     paused;
+       u_int   saved_scbptr;
+
+       /*
+        * Traverse the pending SCB list and ensure that all of the
+        * SCBs there have the proper settings.
+        */
+       pending_scb_count = 0;
+       LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+               struct ahc_devinfo devinfo;
+               struct hardware_scb *pending_hscb;
+               struct ahc_initiator_tinfo *tinfo;
+               struct ahc_tmode_tstate *tstate;
+
+               ahc_scb_devinfo(ahc, &devinfo, pending_scb);
+               tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
+                                           devinfo.our_scsiid,
+                                           devinfo.target, &tstate);
+               pending_hscb = pending_scb->hscb;
+               pending_hscb->control &= ~ULTRAENB;
+               if ((tstate->ultraenb & devinfo.target_mask) != 0)
+                       pending_hscb->control |= ULTRAENB;
+               pending_hscb->scsirate = tinfo->scsirate;
+               pending_hscb->scsioffset = tinfo->curr.offset;
+               if ((tstate->auto_negotiate & devinfo.target_mask) == 0
+                && (pending_scb->flags & SCB_AUTO_NEGOTIATE) != 0) {
+                       pending_scb->flags &= ~SCB_AUTO_NEGOTIATE;
+                       pending_hscb->control &= ~MK_MESSAGE;
+               }
+               ahc_sync_scb(ahc, pending_scb,
+                            BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+               pending_scb_count++;
+       }
+
+       if (pending_scb_count == 0)
+               return;
+
+       if (ahc_is_paused(ahc)) {
+               paused = 1;
+       } else {
+               paused = 0;
+               ahc_pause(ahc);
+       }
+
+       saved_scbptr = ahc_inb(ahc, SCBPTR);
+       /* Ensure that the hscbs down on the card match the new information */
+       for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+               struct  hardware_scb *pending_hscb;
+               u_int   control;
+               u_int   scb_tag;
+
+               ahc_outb(ahc, SCBPTR, i);
+               scb_tag = ahc_inb(ahc, SCB_TAG);
+               pending_scb = ahc_lookup_scb(ahc, scb_tag);
+               if (pending_scb == NULL)
+                       continue;
+
+               pending_hscb = pending_scb->hscb;
+               control = ahc_inb(ahc, SCB_CONTROL);
+               control &= ~(ULTRAENB|MK_MESSAGE);
+               control |= pending_hscb->control & (ULTRAENB|MK_MESSAGE);
+               ahc_outb(ahc, SCB_CONTROL, control);
+               ahc_outb(ahc, SCB_SCSIRATE, pending_hscb->scsirate);
+               ahc_outb(ahc, SCB_SCSIOFFSET, pending_hscb->scsioffset);
+       }
+       ahc_outb(ahc, SCBPTR, saved_scbptr);
+
+       if (paused == 0)
+               ahc_unpause(ahc);
+}
+
+/**************************** Pathing Information *****************************/
+static void
+ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+       u_int   saved_scsiid;
+       role_t  role;
+       int     our_id;
+
+       if (ahc_inb(ahc, SSTAT0) & TARGET)
+               role = ROLE_TARGET;
+       else
+               role = ROLE_INITIATOR;
+
+       if (role == ROLE_TARGET
+        && (ahc->features & AHC_MULTI_TID) != 0
+        && (ahc_inb(ahc, SEQ_FLAGS) & CMDPHASE_PENDING) != 0) {
+               /* We were selected, so pull our id from TARGIDIN */
+               our_id = ahc_inb(ahc, TARGIDIN) & OID;
+       } else if ((ahc->features & AHC_ULTRA2) != 0)
+               our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
+       else
+               our_id = ahc_inb(ahc, SCSIID) & OID;
+
+       saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+       ahc_compile_devinfo(devinfo,
+                           our_id,
+                           SCSIID_TARGET(ahc, saved_scsiid),
+                           ahc_inb(ahc, SAVED_LUN),
+                           SCSIID_CHANNEL(ahc, saved_scsiid),
+                           role);
+}
+
+struct ahc_phase_table_entry*
+ahc_lookup_phase_entry(int phase)
+{
+       struct ahc_phase_table_entry *entry;
+       struct ahc_phase_table_entry *last_entry;
+
+       /*
+        * num_phases doesn't include the default entry which
+        * will be returned if the phase doesn't match.
+        */
+       last_entry = &ahc_phase_table[num_phases];
+       for (entry = ahc_phase_table; entry < last_entry; entry++) {
+               if (phase == entry->phase)
+                       break;
+       }
+       return (entry);
+}
+
+void
+ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int our_id, u_int target,
+                   u_int lun, char channel, role_t role)
+{
+       devinfo->our_scsiid = our_id;
+       devinfo->target = target;
+       devinfo->lun = lun;
+       devinfo->target_offset = target;
+       devinfo->channel = channel;
+       devinfo->role = role;
+       if (channel == 'B')
+               devinfo->target_offset += 8;
+       devinfo->target_mask = (0x01 << devinfo->target_offset);
+}
+
+static void
+ahc_scb_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+               struct scb *scb)
+{
+       role_t  role;
+       int     our_id;
+
+       our_id = SCSIID_OUR_ID(scb->hscb->scsiid);
+       role = ROLE_INITIATOR;
+       if ((scb->hscb->control & TARGET_SCB) != 0)
+               role = ROLE_TARGET;
+       ahc_compile_devinfo(devinfo, our_id, SCB_GET_TARGET(ahc, scb),
+                           SCB_GET_LUN(scb), SCB_GET_CHANNEL(ahc, scb), role);
+}
+
+
+/************************ Message Phase Processing ****************************/
+static void
+ahc_assert_atn(struct ahc_softc *ahc)
+{
+       u_int scsisigo;
+
+       scsisigo = ATNO;
+       if ((ahc->features & AHC_DT) == 0)
+               scsisigo |= ahc_inb(ahc, SCSISIGI);
+       ahc_outb(ahc, SCSISIGO, scsisigo);
+}
+
+/*
+ * When an initiator transaction with the MK_MESSAGE flag either reconnects
+ * or enters the initial message out phase, we are interrupted.  Fill our
+ * outgoing message buffer with the appropriate message and beging handing
+ * the message phase(s) manually.
+ */
+static void
+ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+                          struct scb *scb)
+{
+       /*
+        * To facilitate adding multiple messages together,
+        * each routine should increment the index and len
+        * variables instead of setting them explicitly.
+        */
+       ahc->msgout_index = 0;
+       ahc->msgout_len = 0;
+
+       if ((scb->flags & SCB_DEVICE_RESET) == 0
+        && ahc_inb(ahc, MSG_OUT) == MSG_IDENTIFYFLAG) {
+               u_int identify_msg;
+
+               identify_msg = MSG_IDENTIFYFLAG | SCB_GET_LUN(scb);
+               if ((scb->hscb->control & DISCENB) != 0)
+                       identify_msg |= MSG_IDENTIFY_DISCFLAG;
+               ahc->msgout_buf[ahc->msgout_index++] = identify_msg;
+               ahc->msgout_len++;
+
+               if ((scb->hscb->control & TAG_ENB) != 0) {
+                       ahc->msgout_buf[ahc->msgout_index++] =
+                           scb->hscb->control & (TAG_ENB|SCB_TAG_TYPE);
+                       ahc->msgout_buf[ahc->msgout_index++] = scb->hscb->tag;
+                       ahc->msgout_len += 2;
+               }
+       }
+
+       if (scb->flags & SCB_DEVICE_RESET) {
+               ahc->msgout_buf[ahc->msgout_index++] = MSG_BUS_DEV_RESET;
+               ahc->msgout_len++;
+               ahc_print_path(ahc, scb);
+               printf("Bus Device Reset Message Sent\n");
+               /*
+                * Clear our selection hardware in advance of
+                * the busfree.  We may have an entry in the waiting
+                * Q for this target, and we don't want to go about
+                * selecting while we handle the busfree and blow it
+                * away.
+                */
+               ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+       } else if ((scb->flags & SCB_ABORT) != 0) {
+               if ((scb->hscb->control & TAG_ENB) != 0)
+                       ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT_TAG;
+               else
+                       ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT;
+               ahc->msgout_len++;
+               ahc_print_path(ahc, scb);
+               printf("Abort%s Message Sent\n",
+                      (scb->hscb->control & TAG_ENB) != 0 ? " Tag" : "");
+               /*
+                * Clear our selection hardware in advance of
+                * the busfree.  We may have an entry in the waiting
+                * Q for this target, and we don't want to go about
+                * selecting while we handle the busfree and blow it
+                * away.
+                */
+               ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+       } else if ((scb->flags & (SCB_AUTO_NEGOTIATE|SCB_NEGOTIATE)) != 0) {
+               ahc_build_transfer_msg(ahc, devinfo);
+       } else {
+               printf("ahc_intr: AWAITING_MSG for an SCB that "
+                      "does not have a waiting message\n");
+               printf("SCSIID = %x, target_mask = %x\n", scb->hscb->scsiid,
+                      devinfo->target_mask);
+               panic("SCB = %d, SCB Control = %x, MSG_OUT = %x "
+                     "SCB flags = %x", scb->hscb->tag, scb->hscb->control,
+                     ahc_inb(ahc, MSG_OUT), scb->flags);
+       }
+
+       /*
+        * Clear the MK_MESSAGE flag from the SCB so we aren't
+        * asked to send this message again.
+        */
+       ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL) & ~MK_MESSAGE);
+       scb->hscb->control &= ~MK_MESSAGE;
+       ahc->msgout_index = 0;
+       ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+}
+
+/*
+ * Build an appropriate transfer negotiation message for the
+ * currently active target.
+ */
+static void
+ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+       /*
+        * We need to initiate transfer negotiations.
+        * If our current and goal settings are identical,
+        * we want to renegotiate due to a check condition.
+        */
+       struct  ahc_initiator_tinfo *tinfo;
+       struct  ahc_tmode_tstate *tstate;
+       struct  ahc_syncrate *rate;
+       int     dowide;
+       int     dosync;
+       int     doppr;
+       int     use_ppr;
+       u_int   period;
+       u_int   ppr_options;
+       u_int   offset;
+
+       tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+                                   devinfo->target, &tstate);
+       /*
+        * Filter our period based on the current connection.
+        * If we can't perform DT transfers on this segment (not in LVD
+        * mode for instance), then our decision to issue a PPR message
+        * may change.
+        */
+       period = tinfo->goal.period;
+       ppr_options = tinfo->goal.ppr_options;
+       /* Target initiated PPR is not allowed in the SCSI spec */
+       if (devinfo->role == ROLE_TARGET)
+               ppr_options = 0;
+       rate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+                                      &ppr_options, devinfo->role);
+       dowide = tinfo->curr.width != tinfo->goal.width;
+       dosync = tinfo->curr.period != period;
+       doppr = tinfo->curr.ppr_options != ppr_options;
+
+       if (!dowide && !dosync && !doppr) {
+               dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
+               dosync = tinfo->goal.period != 0;
+               doppr = tinfo->goal.ppr_options != 0;
+       }
+
+       if (!dowide && !dosync && !doppr) {
+               panic("ahc_intr: AWAITING_MSG for negotiation, "
+                     "but no negotiation needed\n");   
+       }
+
+       use_ppr = (tinfo->curr.transport_version >= 3) || doppr;
+       /* Target initiated PPR is not allowed in the SCSI spec */
+       if (devinfo->role == ROLE_TARGET)
+               use_ppr = 0;
+
+       /*
+        * Both the PPR message and SDTR message require the
+        * goal syncrate to be limited to what the target device
+        * is capable of handling (based on whether an LVD->SE
+        * expander is on the bus), so combine these two cases.
+        * Regardless, guarantee that if we are using WDTR and SDTR
+        * messages that WDTR comes first.
+        */
+       if (use_ppr || (dosync && !dowide)) {
+
+               offset = tinfo->goal.offset;
+               ahc_validate_offset(ahc, tinfo, rate, &offset,
+                                   use_ppr ? tinfo->goal.width
+                                           : tinfo->curr.width,
+                                   devinfo->role);
+               if (use_ppr) {
+                       ahc_construct_ppr(ahc, devinfo, period, offset,
+                                         tinfo->goal.width, ppr_options);
+               } else {
+                       ahc_construct_sdtr(ahc, devinfo, period, offset);
+               }
+       } else {
+               ahc_construct_wdtr(ahc, devinfo, tinfo->goal.width);
+       }
+}
+
+/*
+ * Build a synchronous negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_sdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+                  u_int period, u_int offset)
+{
+       ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+       ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR_LEN;
+       ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR;
+       ahc->msgout_buf[ahc->msgout_index++] = period;
+       ahc->msgout_buf[ahc->msgout_index++] = offset;
+       ahc->msgout_len += 5;
+       if (bootverbose) {
+               printf("(%s:%c:%d:%d): Sending SDTR period %x, offset %x\n",
+                      ahc_name(ahc), devinfo->channel, devinfo->target,
+                      devinfo->lun, period, offset);
+       }
+}
+
+/*
+ * Build a wide negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_wdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+                  u_int bus_width)
+{
+       ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+       ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR_LEN;
+       ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR;
+       ahc->msgout_buf[ahc->msgout_index++] = bus_width;
+       ahc->msgout_len += 4;
+       if (bootverbose) {
+               printf("(%s:%c:%d:%d): Sending WDTR %x\n",
+                      ahc_name(ahc), devinfo->channel, devinfo->target,
+                      devinfo->lun, bus_width);
+       }
+}
+
+/*
+ * Build a parallel protocol request message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_ppr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+                 u_int period, u_int offset, u_int bus_width,
+                 u_int ppr_options)
+{
+       ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+       ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR_LEN;
+       ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR;
+       ahc->msgout_buf[ahc->msgout_index++] = period;
+       ahc->msgout_buf[ahc->msgout_index++] = 0;
+       ahc->msgout_buf[ahc->msgout_index++] = offset;
+       ahc->msgout_buf[ahc->msgout_index++] = bus_width;
+       ahc->msgout_buf[ahc->msgout_index++] = ppr_options;
+       ahc->msgout_len += 8;
+       if (bootverbose) {
+               printf("(%s:%c:%d:%d): Sending PPR bus_width %x, period %x, "
+                      "offset %x, ppr_options %x\n", ahc_name(ahc),
+                      devinfo->channel, devinfo->target, devinfo->lun,
+                      bus_width, period, offset, ppr_options);
+       }
+}
+
+/*
+ * Clear any active message state.
+ */
+static void
+ahc_clear_msg_state(struct ahc_softc *ahc)
+{
+       ahc->msgout_len = 0;
+       ahc->msgin_index = 0;
+       ahc->msg_type = MSG_TYPE_NONE;
+       if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0) {
+               /*
+                * The target didn't care to respond to our
+                * message request, so clear ATN.
+                */
+               ahc_outb(ahc, CLRSINT1, CLRATNO);
+       }
+       ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+       ahc_outb(ahc, SEQ_FLAGS2,
+                ahc_inb(ahc, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
+}
+
+/*
+ * Manual message loop handler.
+ */
+static void
+ahc_handle_message_phase(struct ahc_softc *ahc)
+{ 
+       struct  ahc_devinfo devinfo;
+       u_int   bus_phase;
+       int     end_session;
+
+       ahc_fetch_devinfo(ahc, &devinfo);
+       end_session = FALSE;
+       bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+
+reswitch:
+       switch (ahc->msg_type) {
+       case MSG_TYPE_INITIATOR_MSGOUT:
+       {
+               int lastbyte;
+               int phasemis;
+               int msgdone;
+
+               if (ahc->msgout_len == 0)
+                       panic("HOST_MSG_LOOP interrupt with no active message");
+
+               phasemis = bus_phase != P_MESGOUT;
+               if (phasemis) {
+                       if (bus_phase == P_MESGIN) {
+                               /*
+                                * Change gears and see if
+                                * this messages is of interest to
+                                * us or should be passed back to
+                                * the sequencer.
+                                */
+                               ahc_outb(ahc, CLRSINT1, CLRATNO);
+                               ahc->send_msg_perror = FALSE;
+                               ahc->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+                               ahc->msgin_index = 0;
+                               goto reswitch;
+                       }
+                       end_session = TRUE;
+                       break;
+               }
+
+               if (ahc->send_msg_perror) {
+                       ahc_outb(ahc, CLRSINT1, CLRATNO);
+                       ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+                       ahc_outb(ahc, SCSIDATL, MSG_PARITY_ERROR);
+                       break;
+               }
+
+               msgdone = ahc->msgout_index == ahc->msgout_len;
+               if (msgdone) {
+                       /*
+                        * The target has requested a retry.
+                        * Re-assert ATN, reset our message index to
+                        * 0, and try again.
+                        */
+                       ahc->msgout_index = 0;
+                       ahc_assert_atn(ahc);
+               }
+
+               lastbyte = ahc->msgout_index == (ahc->msgout_len - 1);
+               if (lastbyte) {
+                       /* Last byte is signified by dropping ATN */
+                       ahc_outb(ahc, CLRSINT1, CLRATNO);
+               }
+
+               /*
+                * Clear our interrupt status and present
+                * the next byte on the bus.
+                */
+               ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+               ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
+               break;
+       }
+       case MSG_TYPE_INITIATOR_MSGIN:
+       {
+               int phasemis;
+               int message_done;
+
+               phasemis = bus_phase != P_MESGIN;
+
+               if (phasemis) {
+                       ahc->msgin_index = 0;
+                       if (bus_phase == P_MESGOUT
+                        && (ahc->send_msg_perror == TRUE
+                         || (ahc->msgout_len != 0
+                          && ahc->msgout_index == 0))) {
+                               ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+                               goto reswitch;
+                       }
+                       end_session = TRUE;
+                       break;
+               }
+
+               /* Pull the byte in without acking it */
+               ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIBUSL);
+
+               message_done = ahc_parse_msg(ahc, &devinfo);
+
+               if (message_done) {
+                       /*
+                        * Clear our incoming message buffer in case there
+                        * is another message following this one.
+                        */
+                       ahc->msgin_index = 0;
+
+                       /*
+                        * If this message illicited a response,
+                        * assert ATN so the target takes us to the
+                        * message out phase.
+                        */
+                       if (ahc->msgout_len != 0)
+                               ahc_assert_atn(ahc);
+               } else 
+                       ahc->msgin_index++;
+
+               if (message_done == MSGLOOP_TERMINATED) {
+                       end_session = TRUE;
+               } else {
+                       /* Ack the byte */
+                       ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+                       ahc_inb(ahc, SCSIDATL);
+               }
+               break;
+       }
+       case MSG_TYPE_TARGET_MSGIN:
+       {
+               int msgdone;
+               int msgout_request;
+
+               if (ahc->msgout_len == 0)
+                       panic("Target MSGIN with no active message");
+
+               /*
+                * If we interrupted a mesgout session, the initiator
+                * will not know this until our first REQ.  So, we
+                * only honor mesgout requests after we've sent our
+                * first byte.
+                */
+               if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0
+                && ahc->msgout_index > 0)
+                       msgout_request = TRUE;
+               else
+                       msgout_request = FALSE;
+
+               if (msgout_request) {
+
+                       /*
+                        * Change gears and see if
+                        * this messages is of interest to
+                        * us or should be passed back to
+                        * the sequencer.
+                        */
+                       ahc->msg_type = MSG_TYPE_TARGET_MSGOUT;
+                       ahc_outb(ahc, SCSISIGO, P_MESGOUT | BSYO);
+                       ahc->msgin_index = 0;
+                       /* Dummy read to REQ for first byte */
+                       ahc_inb(ahc, SCSIDATL);
+                       ahc_outb(ahc, SXFRCTL0,
+                                ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+                       break;
+               }
+
+               msgdone = ahc->msgout_index == ahc->msgout_len;
+               if (msgdone) {
+                       ahc_outb(ahc, SXFRCTL0,
+                                ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
+                       end_session = TRUE;
+                       break;
+               }
+
+               /*
+                * Present the next byte on the bus.
+                */
+               ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+               ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
+               break;
+       }
+       case MSG_TYPE_TARGET_MSGOUT:
+       {
+               int lastbyte;
+               int msgdone;
+
+               /*
+                * The initiator signals that this is
+                * the last byte by dropping ATN.
+                */
+               lastbyte = (ahc_inb(ahc, SCSISIGI) & ATNI) == 0;
+
+               /*
+                * Read the latched byte, but turn off SPIOEN first
+                * so that we don't inadvertently cause a REQ for the
+                * next byte.
+                */
+               ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
+               ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIDATL);
+               msgdone = ahc_parse_msg(ahc, &devinfo);
+               if (msgdone == MSGLOOP_TERMINATED) {
+                       /*
+                        * The message is *really* done in that it caused
+                        * us to go to bus free.  The sequencer has already
+                        * been reset at this point, so pull the ejection
+                        * handle.
+                        */
+                       return;
+               }
+               
+               ahc->msgin_index++;
+
+               /*
+                * XXX Read spec about initiator dropping ATN too soon
+                *     and use msgdone to detect it.
+                */
+               if (msgdone == MSGLOOP_MSGCOMPLETE) {
+                       ahc->msgin_index = 0;
+
+                       /*
+                        * If this message illicited a response, transition
+                        * to the Message in phase and send it.
+                        */
+                       if (ahc->msgout_len != 0) {
+                               ahc_outb(ahc, SCSISIGO, P_MESGIN | BSYO);
+                               ahc_outb(ahc, SXFRCTL0,
+                                        ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+                               ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+                               ahc->msgin_index = 0;
+                               break;
+                       }
+               }
+
+               if (lastbyte)
+                       end_session = TRUE;
+               else {
+                       /* Ask for the next byte. */
+                       ahc_outb(ahc, SXFRCTL0,
+                                ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+               }
+
+               break;
+       }
+       default:
+               panic("Unknown REQINIT message type");
+       }
+
+       if (end_session) {
+               ahc_clear_msg_state(ahc);
+               ahc_outb(ahc, RETURN_1, EXIT_MSG_LOOP);
+       } else
+               ahc_outb(ahc, RETURN_1, CONT_MSG_LOOP);
+}
+
+/*
+ * See if we sent a particular extended message to the target.
+ * If "full" is true, return true only if the target saw the full
+ * message.  If "full" is false, return true if the target saw at
+ * least the first byte of the message.
+ */
+static int
+ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type, u_int msgval, int full)
+{
+       int found;
+       u_int index;
+
+       found = FALSE;
+       index = 0;
+
+       while (index < ahc->msgout_len) {
+               if (ahc->msgout_buf[index] == MSG_EXTENDED) {
+                       u_int end_index;
+
+                       end_index = index + 1 + ahc->msgout_buf[index + 1];
+                       if (ahc->msgout_buf[index+2] == msgval
+                        && type == AHCMSG_EXT) {
+
+                               if (full) {
+                                       if (ahc->msgout_index > end_index)
+                                               found = TRUE;
+                               } else if (ahc->msgout_index > index)
+                                       found = TRUE;
+                       }
+                       index = end_index;
+               } else if (ahc->msgout_buf[index] >= MSG_SIMPLE_TASK
+                       && ahc->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
+
+                       /* Skip tag type and tag id or residue param*/
+                       index += 2;
+               } else {
+                       /* Single byte message */
+                       if (type == AHCMSG_1B
+                        && ahc->msgout_buf[index] == msgval
+                        && ahc->msgout_index > index)
+                               found = TRUE;
+                       index++;
+               }
+
+               if (found)
+                       break;
+       }
+       return (found);
+}
+
+/*
+ * Wait for a complete incoming message, parse it, and respond accordingly.
+ */
+static int
+ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+       struct  ahc_initiator_tinfo *tinfo;
+       struct  ahc_tmode_tstate *tstate;
+       int     reject;
+       int     done;
+       int     response;
+       u_int   targ_scsirate;
+
+       done = MSGLOOP_IN_PROG;
+       response = FALSE;
+       reject = FALSE;
+       tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+                                   devinfo->target, &tstate);
+       targ_scsirate = tinfo->scsirate;
+
+       /*
+        * Parse as much of the message as is availible,
+        * rejecting it if we don't support it.  When
+        * the entire message is availible and has been
+        * handled, return MSGLOOP_MSGCOMPLETE, indicating
+        * that we have parsed an entire message.
+        *
+        * In the case of extended messages, we accept the length
+        * byte outright and perform more checking once we know the
+        * extended message type.
+        */
+       switch (ahc->msgin_buf[0]) {
+       case MSG_DISCONNECT:
+       case MSG_SAVEDATAPOINTER:
+       case MSG_CMDCOMPLETE:
+       case MSG_RESTOREPOINTERS:
+       case MSG_IGN_WIDE_RESIDUE:
+               /*
+                * End our message loop as these are messages
+                * the sequencer handles on its own.
+                */
+               done = MSGLOOP_TERMINATED;
+               break;
+       case MSG_MESSAGE_REJECT:
+               response = ahc_handle_msg_reject(ahc, devinfo);
+               /* FALLTHROUGH */
+       case MSG_NOOP:
+               done = MSGLOOP_MSGCOMPLETE;
+               break;
+       case MSG_EXTENDED:
+       {
+               /* Wait for enough of the message to begin validation */
+               if (ahc->msgin_index < 2)
+                       break;
+               switch (ahc->msgin_buf[2]) {
+               case MSG_EXT_SDTR:
+               {
+                       struct   ahc_syncrate *syncrate;
+                       u_int    period;
+                       u_int    ppr_options;
+                       u_int    offset;
+                       u_int    saved_offset;
+                       
+                       if (ahc->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
+                               reject = TRUE;
+                               break;
+                       }
+
+                       /*
+                        * Wait until we have both args before validating
+                        * and acting on this message.
+                        *
+                        * Add one to MSG_EXT_SDTR_LEN to account for
+                        * the extended message preamble.
+                        */
+                       if (ahc->msgin_index < (MSG_EXT_SDTR_LEN + 1))
+                               break;
+
+                       period = ahc->msgin_buf[3];
+                       ppr_options = 0;
+                       saved_offset = offset = ahc->msgin_buf[4];
+                       syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+                                                          &ppr_options,
+                                                          devinfo->role);
+                       ahc_validate_offset(ahc, tinfo, syncrate, &offset,
+                                           targ_scsirate & WIDEXFER,
+                                           devinfo->role);
+                       if (bootverbose) {
+                               printf("(%s:%c:%d:%d): Received "
+                                      "SDTR period %x, offset %x\n\t"
+                                      "Filtered to period %x, offset %x\n",
+                                      ahc_name(ahc), devinfo->channel,
+                                      devinfo->target, devinfo->lun,
+                                      ahc->msgin_buf[3], saved_offset,
+                                      period, offset);
+                       }
+                       ahc_set_syncrate(ahc, devinfo, 
+                                        syncrate, period,
+                                        offset, ppr_options,
+                                        AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+                                        /*paused*/TRUE);
+
+                       /*
+                        * See if we initiated Sync Negotiation
+                        * and didn't have to fall down to async
+                        * transfers.
+                        */
+                       if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, TRUE)) {
+                               /* We started it */
+                               if (saved_offset != offset) {
+                                       /* Went too low - force async */
+                                       reject = TRUE;
+                               }
+                       } else {
+                               /*
+                                * Send our own SDTR in reply
+                                */
+                               if (bootverbose
+                                && devinfo->role == ROLE_INITIATOR) {
+                                       printf("(%s:%c:%d:%d): Target "
+                                              "Initiated SDTR\n",
+                                              ahc_name(ahc), devinfo->channel,
+                                              devinfo->target, devinfo->lun);
+                               }
+                               ahc->msgout_index = 0;
+                               ahc->msgout_len = 0;
+                               ahc_construct_sdtr(ahc, devinfo,
+                                                  period, offset);
+                               ahc->msgout_index = 0;
+                               response = TRUE;
+                       }
+                       done = MSGLOOP_MSGCOMPLETE;
+                       break;
+               }
+               case MSG_EXT_WDTR:
+               {
+                       u_int bus_width;
+                       u_int saved_width;
+                       u_int sending_reply;
+
+                       sending_reply = FALSE;
+                       if (ahc->msgin_buf[1] != MSG_EXT_WDTR_LEN) {
+                               reject = TRUE;
+                               break;
+                       }
+
+                       /*
+                        * Wait until we have our arg before validating
+                        * and acting on this message.
+                        *
+                        * Add one to MSG_EXT_WDTR_LEN to account for
+                        * the extended message preamble.
+                        */
+                       if (ahc->msgin_index < (MSG_EXT_WDTR_LEN + 1))
+                               break;
+
+                       bus_width = ahc->msgin_buf[3];
+                       saved_width = bus_width;
+                       ahc_validate_width(ahc, tinfo, &bus_width,
+                                          devinfo->role);
+                       if (bootverbose) {
+                               printf("(%s:%c:%d:%d): Received WDTR "
+                                      "%x filtered to %x\n",
+                                      ahc_name(ahc), devinfo->channel,
+                                      devinfo->target, devinfo->lun,
+                                      saved_width, bus_width);
+                       }
+
+                       if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, TRUE)) {
+                               /*
+                                * Don't send a WDTR back to the
+                                * target, since we asked first.
+                                * If the width went higher than our
+                                * request, reject it.
+                                */
+                               if (saved_width > bus_width) {
+                                       reject = TRUE;
+                                       printf("(%s:%c:%d:%d): requested %dBit "
+                                              "transfers.  Rejecting...\n",
+                                              ahc_name(ahc), devinfo->channel,
+                                              devinfo->target, devinfo->lun,
+                                              8 * (0x01 << bus_width));
+                                       bus_width = 0;
+                               }
+                       } else {
+                               /*
+                                * Send our own WDTR in reply
+                                */
+                               if (bootverbose
+                                && devinfo->role == ROLE_INITIATOR) {
+                                       printf("(%s:%c:%d:%d): Target "
+                                              "Initiated WDTR\n",
+                                              ahc_name(ahc), devinfo->channel,
+                                              devinfo->target, devinfo->lun);
+                               }
+                               ahc->msgout_index = 0;
+                               ahc->msgout_len = 0;
+                               ahc_construct_wdtr(ahc, devinfo, bus_width);
+                               ahc->msgout_index = 0;
+                               response = TRUE;
+                               sending_reply = TRUE;
+                       }
+                       ahc_set_width(ahc, devinfo, bus_width,
+                                     AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+                                     /*paused*/TRUE);
+                       /* After a wide message, we are async */
+                       ahc_set_syncrate(ahc, devinfo,
+                                        /*syncrate*/NULL, /*period*/0,
+                                        /*offset*/0, /*ppr_options*/0,
+                                        AHC_TRANS_ACTIVE, /*paused*/TRUE);
+                       if (sending_reply == FALSE && reject == FALSE) {
+
+                               if (tinfo->goal.period) {
+                                       ahc->msgout_index = 0;
+                                       ahc->msgout_len = 0;
+                                       ahc_build_transfer_msg(ahc, devinfo);
+                                       ahc->msgout_index = 0;
+                                       response = TRUE;
+                               }
+                       }
+                       done = MSGLOOP_MSGCOMPLETE;
+                       break;
+               }
+               case MSG_EXT_PPR:
+               {
+                       struct  ahc_syncrate *syncrate;
+                       u_int   period;
+                       u_int   offset;
+                       u_int   bus_width;
+                       u_int   ppr_options;
+                       u_int   saved_width;
+                       u_int   saved_offset;
+                       u_int   saved_ppr_options;
+
+                       if (ahc->msgin_buf[1] != MSG_EXT_PPR_LEN) {
+                               reject = TRUE;
+                               break;
+                       }
+
+                       /*
+                        * Wait until we have all args before validating
+                        * and acting on this message.
+                        *
+                        * Add one to MSG_EXT_PPR_LEN to account for
+                        * the extended message preamble.
+                        */
+                       if (ahc->msgin_index < (MSG_EXT_PPR_LEN + 1))
+                               break;
+
+                       period = ahc->msgin_buf[3];
+                       offset = ahc->msgin_buf[5];
+                       bus_width = ahc->msgin_buf[6];
+                       saved_width = bus_width;
+                       ppr_options = ahc->msgin_buf[7];
+                       /*
+                        * According to the spec, a DT only
+                        * period factor with no DT option
+                        * set implies async.
+                        */
+                       if ((ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+                        && period == 9)
+                               offset = 0;
+                       saved_ppr_options = ppr_options;
+                       saved_offset = offset;
+
+                       /*
+                        * Mask out any options we don't support
+                        * on any controller.  Transfer options are
+                        * only available if we are negotiating wide.
+                        */
+                       ppr_options &= MSG_EXT_PPR_DT_REQ;
+                       if (bus_width == 0)
+                               ppr_options = 0;
+
+                       ahc_validate_width(ahc, tinfo, &bus_width,
+                                          devinfo->role);
+                       syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+                                                          &ppr_options,
+                                                          devinfo->role);
+                       ahc_validate_offset(ahc, tinfo, syncrate,
+                                           &offset, bus_width,
+                                           devinfo->role);
+
+                       if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, TRUE)) {
+                               /*
+                                * If we are unable to do any of the
+                                * requested options (we went too low),
+                                * then we'll have to reject the message.
+                                */
+                               if (saved_width > bus_width
+                                || saved_offset != offset
+                                || saved_ppr_options != ppr_options) {
+                                       reject = TRUE;
+                                       period = 0;
+                                       offset = 0;
+                                       bus_width = 0;
+                                       ppr_options = 0;
+                                       syncrate = NULL;
+                               }
+                       } else {
+                               if (devinfo->role != ROLE_TARGET)
+                                       printf("(%s:%c:%d:%d): Target "
+                                              "Initiated PPR\n",
+                                              ahc_name(ahc), devinfo->channel,
+                                              devinfo->target, devinfo->lun);
+                               else
+                                       printf("(%s:%c:%d:%d): Initiator "
+                                              "Initiated PPR\n",
+                                              ahc_name(ahc), devinfo->channel,
+                                              devinfo->target, devinfo->lun);
+                               ahc->msgout_index = 0;
+                               ahc->msgout_len = 0;
+                               ahc_construct_ppr(ahc, devinfo, period, offset,
+                                                 bus_width, ppr_options);
+                               ahc->msgout_index = 0;
+                               response = TRUE;
+                       }
+                       if (bootverbose) {
+                               printf("(%s:%c:%d:%d): Received PPR width %x, "
+                                      "period %x, offset %x,options %x\n"
+                                      "\tFiltered to width %x, period %x, "
+                                      "offset %x, options %x\n",
+                                      ahc_name(ahc), devinfo->channel,
+                                      devinfo->target, devinfo->lun,
+                                      saved_width, ahc->msgin_buf[3],
+                                      saved_offset, saved_ppr_options,
+                                      bus_width, period, offset, ppr_options);
+                       }
+                       ahc_set_width(ahc, devinfo, bus_width,
+                                     AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+                                     /*paused*/TRUE);
+                       ahc_set_syncrate(ahc, devinfo,
+                                        syncrate, period,
+                                        offset, ppr_options,
+                                        AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+                                        /*paused*/TRUE);
+                       done = MSGLOOP_MSGCOMPLETE;
+                       break;
+               }
+               default:
+                       /* Unknown extended message.  Reject it. */
+                       reject = TRUE;
+                       break;
+               }
+               break;
+       }
+#ifdef AHC_TARGET_MODE
+       case MSG_BUS_DEV_RESET:
+               ahc_handle_devreset(ahc, devinfo,
+                                   CAM_BDR_SENT,
+                                   "Bus Device Reset Received",
+                                   /*verbose_level*/0);
+               ahc_restart(ahc);
+               done = MSGLOOP_TERMINATED;
+               break;
+       case MSG_ABORT_TAG:
+       case MSG_ABORT:
+       case MSG_CLEAR_QUEUE:
+       {
+               int tag;
+
+               /* Target mode messages */
+               if (devinfo->role != ROLE_TARGET) {
+                       reject = TRUE;
+                       break;
+               }
+               tag = SCB_LIST_NULL;
+               if (ahc->msgin_buf[0] == MSG_ABORT_TAG)
+                       tag = ahc_inb(ahc, INITIATOR_TAG);
+               ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
+                              devinfo->lun, tag, ROLE_TARGET,
+                              CAM_REQ_ABORTED);
+
+               tstate = ahc->enabled_targets[devinfo->our_scsiid];
+               if (tstate != NULL) {
+                       struct ahc_tmode_lstate* lstate;
+
+                       lstate = tstate->enabled_luns[devinfo->lun];
+                       if (lstate != NULL) {
+                               ahc_queue_lstate_event(ahc, lstate,
+                                                      devinfo->our_scsiid,
+                                                      ahc->msgin_buf[0],
+                                                      /*arg*/tag);
+                               ahc_send_lstate_events(ahc, lstate);
+                       }
+               }
+               ahc_restart(ahc);
+               done = MSGLOOP_TERMINATED;
+               break;
+       }
+#endif
+       case MSG_TERM_IO_PROC:
+       default:
+               reject = TRUE;
+               break;
+       }
+
+       if (reject) {
+               /*
+                * Setup to reject the message.
+                */
+               ahc->msgout_index = 0;
+               ahc->msgout_len = 1;
+               ahc->msgout_buf[0] = MSG_MESSAGE_REJECT;
+               done = MSGLOOP_MSGCOMPLETE;
+               response = TRUE;
+       }
+
+       if (done != MSGLOOP_IN_PROG && !response)
+               /* Clear the outgoing message buffer */
+               ahc->msgout_len = 0;
+
+       return (done);
+}
+
+/*
+ * Process a message reject message.
+ */
+static int
+ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+       /*
+        * What we care about here is if we had an
+        * outstanding SDTR or WDTR message for this
+        * target.  If we did, this is a signal that
+        * the target is refusing negotiation.
+        */
+       struct scb *scb;
+       struct ahc_initiator_tinfo *tinfo;
+       struct ahc_tmode_tstate *tstate;
+       u_int scb_index;
+       u_int last_msg;
+       int   response = 0;
+
+       scb_index = ahc_inb(ahc, SCB_TAG);
+       scb = ahc_lookup_scb(ahc, scb_index);
+       tinfo = ahc_fetch_transinfo(ahc, devinfo->channel,
+                                   devinfo->our_scsiid,
+                                   devinfo->target, &tstate);
+       /* Might be necessary */
+       last_msg = ahc_inb(ahc, LAST_MSG);
+
+       if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, /*full*/FALSE)) {
+               /*
+                * Target does not support the PPR message.
+                * Attempt to negotiate SPI-2 style.
+                */
+               if (bootverbose) {
+                       printf("(%s:%c:%d:%d): PPR Rejected. "
+                              "Trying WDTR/SDTR\n",
+                              ahc_name(ahc), devinfo->channel,
+                              devinfo->target, devinfo->lun);
+               }
+               tinfo->goal.ppr_options = 0;
+               tinfo->curr.transport_version = 2;
+               tinfo->goal.transport_version = 2;
+               ahc->msgout_index = 0;
+               ahc->msgout_len = 0;
+               ahc_build_transfer_msg(ahc, devinfo);
+               ahc->msgout_index = 0;
+               response = 1;
+       } else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, /*full*/FALSE)) {
+
+               /* note 8bit xfers */
+               printf("(%s:%c:%d:%d): refuses WIDE negotiation.  Using "
+                      "8bit transfers\n", ahc_name(ahc),
+                      devinfo->channel, devinfo->target, devinfo->lun);
+               ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+                             AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+                             /*paused*/TRUE);
+               /*
+                * No need to clear the sync rate.  If the target
+                * did not accept the command, our syncrate is
+                * unaffected.  If the target started the negotiation,
+                * but rejected our response, we already cleared the
+                * sync rate before sending our WDTR.
+                */
+               if (tinfo->goal.period) {
+
+                       /* Start the sync negotiation */
+                       ahc->msgout_index = 0;
+                       ahc->msgout_len = 0;
+                       ahc_build_transfer_msg(ahc, devinfo);
+                       ahc->msgout_index = 0;
+                       response = 1;
+               }
+       } else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, /*full*/FALSE)) {
+               /* note asynch xfers and clear flag */
+               ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL, /*period*/0,
+                                /*offset*/0, /*ppr_options*/0,
+                                AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+                                /*paused*/TRUE);
+               printf("(%s:%c:%d:%d): refuses synchronous negotiation. "
+                      "Using asynchronous transfers\n",
+                      ahc_name(ahc), devinfo->channel,
+                      devinfo->target, devinfo->lun);
+       } else if ((scb->hscb->control & MSG_SIMPLE_TASK) != 0) {
+               int tag_type;
+               int mask;
+
+               tag_type = (scb->hscb->control & MSG_SIMPLE_TASK);
+
+               if (tag_type == MSG_SIMPLE_TASK) {
+                       printf("(%s:%c:%d:%d): refuses tagged commands.  "
+                              "Performing non-tagged I/O\n", ahc_name(ahc),
+                              devinfo->channel, devinfo->target, devinfo->lun);
+                       ahc_set_tags(ahc, devinfo, AHC_QUEUE_NONE);
+                       mask = ~0x23;
+               } else {
+                       printf("(%s:%c:%d:%d): refuses %s tagged commands.  "
+                              "Performing simple queue tagged I/O only\n",
+                              ahc_name(ahc), devinfo->channel, devinfo->target,
+                              devinfo->lun, tag_type == MSG_ORDERED_TASK
+                              ? "ordered" : "head of queue");
+                       ahc_set_tags(ahc, devinfo, AHC_QUEUE_BASIC);
+                       mask = ~0x03;
+               }
+
+               /*
+                * Resend the identify for this CCB as the target
+                * may believe that the selection is invalid otherwise.
+                */
+               ahc_outb(ahc, SCB_CONTROL,
+                        ahc_inb(ahc, SCB_CONTROL) & mask);
+               scb->hscb->control &= mask;
+               ahc_set_transaction_tag(scb, /*enabled*/FALSE,
+                                       /*type*/MSG_SIMPLE_TASK);
+               ahc_outb(ahc, MSG_OUT, MSG_IDENTIFYFLAG);
+               ahc_assert_atn(ahc);
+
+               /*
+                * This transaction is now at the head of
+                * the untagged queue for this target.
+                */
+               if ((ahc->flags & AHC_SCB_BTT) == 0) {
+                       struct scb_tailq *untagged_q;
+
+                       untagged_q =
+                           &(ahc->untagged_queues[devinfo->target_offset]);
+                       TAILQ_INSERT_HEAD(untagged_q, scb, links.tqe);
+                       scb->flags |= SCB_UNTAGGEDQ;
+               }
+               ahc_busy_tcl(ahc, BUILD_TCL(scb->hscb->scsiid, devinfo->lun),
+                            scb->hscb->tag);
+
+               /*
+                * Requeue all tagged commands for this target
+                * currently in our posession so they can be
+                * converted to untagged commands.
+                */
+               ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+                                  SCB_GET_CHANNEL(ahc, scb),
+                                  SCB_GET_LUN(scb), /*tag*/SCB_LIST_NULL,
+                                  ROLE_INITIATOR, CAM_REQUEUE_REQ,
+                                  SEARCH_COMPLETE);
+       } else {
+               /*
+                * Otherwise, we ignore it.
+                */
+               printf("%s:%c:%d: Message reject for %x -- ignored\n",
+                      ahc_name(ahc), devinfo->channel, devinfo->target,
+                      last_msg);
+       }
+       return (response);
+}
+
+/*
+ * Process an ingnore wide residue message.
+ */
+static void
+ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+       u_int scb_index;
+       struct scb *scb;
+
+       scb_index = ahc_inb(ahc, SCB_TAG);
+       scb = ahc_lookup_scb(ahc, scb_index);
+       /*
+        * XXX Actually check data direction in the sequencer?
+        * Perhaps add datadir to some spare bits in the hscb?
+        */
+       if ((ahc_inb(ahc, SEQ_FLAGS) & DPHASE) == 0
+        || ahc_get_transfer_dir(scb) != CAM_DIR_IN) {
+               /*
+                * Ignore the message if we haven't
+                * seen an appropriate data phase yet.
+                */
+       } else {
+               /*
+                * If the residual occurred on the last
+                * transfer and the transfer request was
+                * expected to end on an odd count, do
+                * nothing.  Otherwise, subtract a byte
+                * and update the residual count accordingly.
+                */
+               uint32_t sgptr;
+
+               sgptr = ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
+               if ((sgptr & SG_LIST_NULL) != 0
+                && ahc_inb(ahc, DATA_COUNT_ODD) == 1) {
+                       /*
+                        * If the residual occurred on the last
+                        * transfer and the transfer request was
+                        * expected to end on an odd count, do
+                        * nothing.
+                        */
+               } else {
+                       struct ahc_dma_seg *sg;
+                       uint32_t data_cnt;
+                       uint32_t data_addr;
+                       uint32_t sglen;
+
+                       /* Pull in the rest of the sgptr */
+                       sgptr |= (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 3) << 24)
+                             | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 2) << 16)
+                             | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 1) << 8);
+                       sgptr &= SG_PTR_MASK;
+                       data_cnt = (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+3) << 24)
+                                | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+2) << 16)
+                                | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+1) << 8)
+                                | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT));
+
+                       data_addr = (ahc_inb(ahc, SHADDR + 3) << 24)
+                                 | (ahc_inb(ahc, SHADDR + 2) << 16)
+                                 | (ahc_inb(ahc, SHADDR + 1) << 8)
+                                 | (ahc_inb(ahc, SHADDR));
+
+                       data_cnt += 1;
+                       data_addr -= 1;
+
+                       sg = ahc_sg_bus_to_virt(scb, sgptr);
+                       /*
+                        * The residual sg ptr points to the next S/G
+                        * to load so we must go back one.
+                        */
+                       sg--;
+                       sglen = ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
+                       if (sg != scb->sg_list
+                        && sglen < (data_cnt & AHC_SG_LEN_MASK)) {
+
+                               sg--;
+                               sglen = ahc_le32toh(sg->len);
+                               /*
+                                * Preserve High Address and SG_LIST bits
+                                * while setting the count to 1.
+                                */
+                               data_cnt = 1 | (sglen & (~AHC_SG_LEN_MASK));
+                               data_addr = ahc_le32toh(sg->addr)
+                                         + (sglen & AHC_SG_LEN_MASK) - 1;
+
+                               /*
+                                * Increment sg so it points to the
+                                * "next" sg.
+                                */
+                               sg++;
+                               sgptr = ahc_sg_virt_to_bus(scb, sg);
+                               ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 3,
+                                        sgptr >> 24);
+                               ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 2,
+                                        sgptr >> 16);
+                               ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 1,
+                                        sgptr >> 8);
+                               ahc_outb(ahc, SCB_RESIDUAL_SGPTR, sgptr);
+                       }
+
+                       ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 3, data_cnt >> 24);
+                       ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 2, data_cnt >> 16);
+                       ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 1, data_cnt >> 8);
+                       ahc_outb(ahc, SCB_RESIDUAL_DATACNT, data_cnt);
+               }
+       }
+}
+
+
+/*
+ * Reinitialize the data pointers for the active transfer
+ * based on its current residual.
+ */
+static void
+ahc_reinitialize_dataptrs(struct ahc_softc *ahc)
+{
+       struct   scb *scb;
+       struct   ahc_dma_seg *sg;
+       u_int    scb_index;
+       uint32_t sgptr;
+       uint32_t resid;
+       uint32_t dataptr;
+
+       scb_index = ahc_inb(ahc, SCB_TAG);
+       scb = ahc_lookup_scb(ahc, scb_index);
+       sgptr = (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 3) << 24)
+             | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 2) << 16)
+             | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 1) << 8)
+             | ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
+
+       sgptr &= SG_PTR_MASK;
+       sg = ahc_sg_bus_to_virt(scb, sgptr);
+
+       /* The residual sg_ptr always points to the next sg */
+       sg--;
+
+       resid = (ahc_inb(ahc, SCB_RESIDUAL_DATACNT + 2) << 16)
+             | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT + 1) << 8)
+             | ahc_inb(ahc, SCB_RESIDUAL_DATACNT);
+
+       dataptr = ahc_le32toh(sg->addr)
+               + (ahc_le32toh(sg->len) & AHC_SG_LEN_MASK)
+               - resid;
+       if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+               u_int dscommand1;
+
+               dscommand1 = ahc_inb(ahc, DSCOMMAND1);
+               ahc_outb(ahc, DSCOMMAND1, dscommand1 | HADDLDSEL0);
+               ahc_outb(ahc, HADDR,
+                        (ahc_le32toh(sg->len) >> 24) & SG_HIGH_ADDR_BITS);
+               ahc_outb(ahc, DSCOMMAND1, dscommand1);
+       }
+       ahc_outb(ahc, HADDR + 3, dataptr >> 24);
+       ahc_outb(ahc, HADDR + 2, dataptr >> 16);
+       ahc_outb(ahc, HADDR + 1, dataptr >> 8);
+       ahc_outb(ahc, HADDR, dataptr);
+       ahc_outb(ahc, HCNT + 2, resid >> 16);
+       ahc_outb(ahc, HCNT + 1, resid >> 8);
+       ahc_outb(ahc, HCNT, resid);
+       if ((ahc->features & AHC_ULTRA2) == 0) {
+               ahc_outb(ahc, STCNT + 2, resid >> 16);
+               ahc_outb(ahc, STCNT + 1, resid >> 8);
+               ahc_outb(ahc, STCNT, resid);
+       }
+}
+
+/*
+ * Handle the effects of issuing a bus device reset message.
+ */
+static void
+ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+                   cam_status status, char *message, int verbose_level)
+{
+#ifdef AHC_TARGET_MODE
+       struct ahc_tmode_tstate* tstate;
+       u_int lun;
+#endif
+       int found;
+
+       found = ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
+                              CAM_LUN_WILDCARD, SCB_LIST_NULL, devinfo->role,
+                              status);
+
+#ifdef AHC_TARGET_MODE
+       /*
+        * Send an immediate notify ccb to all target mord peripheral
+        * drivers affected by this action.
+        */
+       tstate = ahc->enabled_targets[devinfo->our_scsiid];
+       if (tstate != NULL) {
+               for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+                       struct ahc_tmode_lstate* lstate;
+
+                       lstate = tstate->enabled_luns[lun];
+                       if (lstate == NULL)
+                               continue;
+
+                       ahc_queue_lstate_event(ahc, lstate, devinfo->our_scsiid,
+                                              MSG_BUS_DEV_RESET, /*arg*/0);
+                       ahc_send_lstate_events(ahc, lstate);
+               }
+       }
+#endif
+
+       /*
+        * Go back to async/narrow transfers and renegotiate.
+        */
+       ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+                     AHC_TRANS_CUR, /*paused*/TRUE);
+       ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL,
+                        /*period*/0, /*offset*/0, /*ppr_options*/0,
+                        AHC_TRANS_CUR, /*paused*/TRUE);
+       
+       ahc_send_async(ahc, devinfo->channel, devinfo->target,
+                      CAM_LUN_WILDCARD, AC_SENT_BDR, NULL);
+
+       if (message != NULL
+        && (verbose_level <= bootverbose))
+               printf("%s: %s on %c:%d. %d SCBs aborted\n", ahc_name(ahc),
+                      message, devinfo->channel, devinfo->target, found);
+}
+
+#ifdef AHC_TARGET_MODE
+static void
+ahc_setup_target_msgin(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+                      struct scb *scb)
+{
+
+       /*              
+        * To facilitate adding multiple messages together,
+        * each routine should increment the index and len
+        * variables instead of setting them explicitly.
+        */             
+       ahc->msgout_index = 0;
+       ahc->msgout_len = 0;
+
+       if (scb != NULL && (scb->flags & SCB_AUTO_NEGOTIATE) != 0)
+               ahc_build_transfer_msg(ahc, devinfo);
+       else
+               panic("ahc_intr: AWAITING target message with no message");
+
+       ahc->msgout_index = 0;
+       ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+}
+#endif
+/**************************** Initialization **********************************/
+/*
+ * Allocate a controller structure for a new device
+ * and perform initial initializion.
+ */
+struct ahc_softc *
+ahc_alloc(void *platform_arg, char *name)
+{
+       struct  ahc_softc *ahc;
+       int     i;
+
+#ifndef        __FreeBSD__
+       ahc = malloc(sizeof(*ahc), M_DEVBUF, M_NOWAIT);
+       if (!ahc) {
+               printf("aic7xxx: cannot malloc softc!\n");
+               free(name, M_DEVBUF);
+               return NULL;
+       }
+#else
+       ahc = device_get_softc((device_t)platform_arg);
+#endif
+       memset(ahc, 0, sizeof(*ahc));
+       ahc->seep_config = malloc(sizeof(*ahc->seep_config),
+                                 M_DEVBUF, M_NOWAIT);
+       if (ahc->seep_config == NULL) {
+#ifndef        __FreeBSD__
+               free(ahc, M_DEVBUF);
+#endif
+               free(name, M_DEVBUF);
+               return (NULL);
+       }
+       LIST_INIT(&ahc->pending_scbs);
+       /* We don't know our unit number until the OSM sets it */
+       ahc->name = name;
+       ahc->unit = -1;
+       ahc->description = NULL;
+       ahc->channel = 'A';
+       ahc->channel_b = 'B';
+       ahc->chip = AHC_NONE;
+       ahc->features = AHC_FENONE;
+       ahc->bugs = AHC_BUGNONE;
+       ahc->flags = AHC_FNONE;
+
+       for (i = 0; i < AHC_NUM_TARGETS; i++)
+               TAILQ_INIT(&ahc->untagged_queues[i]);
+       if (ahc_platform_alloc(ahc, platform_arg) != 0) {
+               ahc_free(ahc);
+               ahc = NULL;
+       }
+       return (ahc);
+}
+
+int
+ahc_softc_init(struct ahc_softc *ahc)
+{
+
+       /* The IRQMS bit is only valid on VL and EISA chips */
+       if ((ahc->chip & AHC_PCI) == 0)
+               ahc->unpause = ahc_inb(ahc, HCNTRL) & IRQMS;
+       else
+               ahc->unpause = 0;
+       ahc->pause = ahc->unpause | PAUSE; 
+       /* XXX The shared scb data stuff should be deprecated */
+       if (ahc->scb_data == NULL) {
+               ahc->scb_data = malloc(sizeof(*ahc->scb_data),
+                                      M_DEVBUF, M_NOWAIT);
+               if (ahc->scb_data == NULL)
+                       return (ENOMEM);
+               memset(ahc->scb_data, 0, sizeof(*ahc->scb_data));
+       }
+
+       return (0);
+}
+
+void
+ahc_softc_insert(struct ahc_softc *ahc)
+{
+       struct ahc_softc *list_ahc;
+
+#if AHC_PCI_CONFIG > 0
+       /*
+        * Second Function PCI devices need to inherit some
+        * settings from function 0.
+        */
+       if ((ahc->chip & AHC_BUS_MASK) == AHC_PCI
+        && (ahc->features & AHC_MULTI_FUNC) != 0) {
+               TAILQ_FOREACH(list_ahc, &ahc_tailq, links) {
+                       ahc_dev_softc_t list_pci;
+                       ahc_dev_softc_t pci;
+
+                       list_pci = list_ahc->dev_softc;
+                       pci = ahc->dev_softc;
+                       if (ahc_get_pci_slot(list_pci) == ahc_get_pci_slot(pci)
+                        && ahc_get_pci_bus(list_pci) == ahc_get_pci_bus(pci)) {
+                               struct ahc_softc *master;
+                               struct ahc_softc *slave;
+
+                               if (ahc_get_pci_function(list_pci) == 0) {
+                                       master = list_ahc;
+                                       slave = ahc;
+                               } else {
+                                       master = ahc;
+                                       slave = list_ahc;
+                               }
+                               slave->flags &= ~AHC_BIOS_ENABLED; 
+                               slave->flags |=
+                                   master->flags & AHC_BIOS_ENABLED;
+                               slave->flags &= ~AHC_PRIMARY_CHANNEL; 
+                               slave->flags |=
+                                   master->flags & AHC_PRIMARY_CHANNEL;
+                               break;
+                       }
+               }
+       }
+#endif
+
+       /*
+        * Insertion sort into our list of softcs.
+        */
+       list_ahc = TAILQ_FIRST(&ahc_tailq);
+       while (list_ahc != NULL
+           && ahc_softc_comp(list_ahc, ahc) <= 0)
+               list_ahc = TAILQ_NEXT(list_ahc, links);
+       if (list_ahc != NULL)
+               TAILQ_INSERT_BEFORE(list_ahc, ahc, links);
+       else
+               TAILQ_INSERT_TAIL(&ahc_tailq, ahc, links);
+       ahc->init_level++;
+}
+
+/*
+ * Verify that the passed in softc pointer is for a
+ * controller that is still configured.
+ */
+struct ahc_softc *
+ahc_find_softc(struct ahc_softc *ahc)
+{
+       struct ahc_softc *list_ahc;
+
+       TAILQ_FOREACH(list_ahc, &ahc_tailq, links) {
+               if (list_ahc == ahc)
+                       return (ahc);
+       }
+       return (NULL);
+}
+
+void
+ahc_set_unit(struct ahc_softc *ahc, int unit)
+{
+       ahc->unit = unit;
+}
+
+void
+ahc_set_name(struct ahc_softc *ahc, char *name)
+{
+       if (ahc->name != NULL)
+               free(ahc->name, M_DEVBUF);
+       ahc->name = name;
+}
+
+void
+ahc_free(struct ahc_softc *ahc)
+{
+       int i;
+
+       ahc_fini_scbdata(ahc);
+       switch (ahc->init_level) {
+       default:
+       case 5:
+               ahc_shutdown(ahc);
+               TAILQ_REMOVE(&ahc_tailq, ahc, links);
+               /* FALLTHROUGH */
+       case 4:
+               ahc_dmamap_unload(ahc, ahc->shared_data_dmat,
+                                 ahc->shared_data_dmamap);
+               /* FALLTHROUGH */
+       case 3:
+               ahc_dmamem_free(ahc, ahc->shared_data_dmat, ahc->qoutfifo,
+                               ahc->shared_data_dmamap);
+               ahc_dmamap_destroy(ahc, ahc->shared_data_dmat,
+                                  ahc->shared_data_dmamap);
+               /* FALLTHROUGH */
+       case 2:
+               ahc_dma_tag_destroy(ahc, ahc->shared_data_dmat);
+       case 1:
+#ifndef __linux__
+               ahc_dma_tag_destroy(ahc, ahc->buffer_dmat);
+#endif
+               break;
+       case 0:
+               break;
+       }
+
+#ifndef __linux__
+       ahc_dma_tag_destroy(ahc, ahc->parent_dmat);
+#endif
+       ahc_platform_free(ahc);
+       for (i = 0; i < AHC_NUM_TARGETS; i++) {
+               struct ahc_tmode_tstate *tstate;
+
+               tstate = ahc->enabled_targets[i];
+               if (tstate != NULL) {
+#if AHC_TARGET_MODE
+                       int j;
+
+                       for (j = 0; j < AHC_NUM_LUNS; j++) {
+                               struct ahc_tmode_lstate *lstate;
+
+                               lstate = tstate->enabled_luns[j];
+                               if (lstate != NULL) {
+                                       xpt_free_path(lstate->path);
+                                       free(lstate, M_DEVBUF);
+                               }
+                       }
+#endif
+                       free(tstate, M_DEVBUF);
+               }
+       }
+#if AHC_TARGET_MODE
+       if (ahc->black_hole != NULL) {
+               xpt_free_path(ahc->black_hole->path);
+               free(ahc->black_hole, M_DEVBUF);
+       }
+#endif
+       if (ahc->name != NULL)
+               free(ahc->name, M_DEVBUF);
+       if (ahc->seep_config != NULL)
+               free(ahc->seep_config, M_DEVBUF);
+#ifndef __FreeBSD__
+       free(ahc, M_DEVBUF);
+#endif
+       return;
+}
+
+void
+ahc_shutdown(void *arg)
+{
+       struct  ahc_softc *ahc;
+       int     i;
+
+       ahc = (struct ahc_softc *)arg;
+
+       /* This will reset most registers to 0, but not all */
+       ahc_reset(ahc);
+       ahc_outb(ahc, SCSISEQ, 0);
+       ahc_outb(ahc, SXFRCTL0, 0);
+       ahc_outb(ahc, DSPCISTATUS, 0);
+
+       for (i = TARG_SCSIRATE; i < SCSICONF; i++)
+               ahc_outb(ahc, i, 0);
+}
+
+/*
+ * Reset the controller and record some information about it
+ * that is only available just after a reset.
+ */
+int
+ahc_reset(struct ahc_softc *ahc)
+{
+       u_int   sblkctl;
+       u_int   sxfrctl1_a, sxfrctl1_b;
+       int     wait;
+       
+       /*
+        * Preserve the value of the SXFRCTL1 register for all channels.
+        * It contains settings that affect termination and we don't want
+        * to disturb the integrity of the bus.
+        */
+       ahc_pause(ahc);
+       sxfrctl1_b = 0;
+       if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7770) {
+               u_int sblkctl;
+
+               /*
+                * Save channel B's settings in case this chip
+                * is setup for TWIN channel operation.
+                */
+               sblkctl = ahc_inb(ahc, SBLKCTL);
+               ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
+               sxfrctl1_b = ahc_inb(ahc, SXFRCTL1);
+               ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
+       }
+       sxfrctl1_a = ahc_inb(ahc, SXFRCTL1);
+
+       ahc_outb(ahc, HCNTRL, CHIPRST | ahc->pause);
+
+       /*
+        * Ensure that the reset has finished.  We delay 1000us
+        * prior to reading the register to make sure the chip
+        * has sufficiently completed its reset to handle register
+        * accesses.
+        */
+       wait = 1000;
+       do {
+               ahc_delay(1000);
+       } while (--wait && !(ahc_inb(ahc, HCNTRL) & CHIPRSTACK));
+
+       if (wait == 0) {
+               printf("%s: WARNING - Failed chip reset!  "
+                      "Trying to initialize anyway.\n", ahc_name(ahc));
+       }
+       ahc_outb(ahc, HCNTRL, ahc->pause);
+
+       /* Determine channel configuration */
+       sblkctl = ahc_inb(ahc, SBLKCTL) & (SELBUSB|SELWIDE);
+       /* No Twin Channel PCI cards */
+       if ((ahc->chip & AHC_PCI) != 0)
+               sblkctl &= ~SELBUSB;
+       switch (sblkctl) {
+       case 0:
+               /* Single Narrow Channel */
+               break;
+       case 2:
+               /* Wide Channel */
+               ahc->features |= AHC_WIDE;
+               break;
+       case 8:
+               /* Twin Channel */
+               ahc->features |= AHC_TWIN;
+               break;
+       default:
+               printf(" Unsupported adapter type.  Ignoring\n");
+               return(-1);
+       }
+
+       /*
+        * Reload sxfrctl1.
+        *
+        * We must always initialize STPWEN to 1 before we
+        * restore the saved values.  STPWEN is initialized
+        * to a tri-state condition which can only be cleared
+        * by turning it on.
+        */
+       if ((ahc->features & AHC_TWIN) != 0) {
+               u_int sblkctl;
+
+               sblkctl = ahc_inb(ahc, SBLKCTL);
+               ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
+               ahc_outb(ahc, SXFRCTL1, sxfrctl1_b);
+               ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
+       }
+       ahc_outb(ahc, SXFRCTL1, sxfrctl1_a);
+
+#ifdef AHC_DUMP_SEQ
+       if (ahc->init_level == 0)
+               ahc_dumpseq(ahc);
+#endif
+
+       return (0);
+}
+
+/*
+ * Determine the number of SCBs available on the controller
+ */
+int
+ahc_probe_scbs(struct ahc_softc *ahc) {
+       int i;
+
+       for (i = 0; i < AHC_SCB_MAX; i++) {
+
+               ahc_outb(ahc, SCBPTR, i);
+               ahc_outb(ahc, SCB_BASE, i);
+               if (ahc_inb(ahc, SCB_BASE) != i)
+                       break;
+               ahc_outb(ahc, SCBPTR, 0);
+               if (ahc_inb(ahc, SCB_BASE) != 0)
+                       break;
+       }
+       return (i);
+}
+
+static void
+ahc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 
+{
+       bus_addr_t *baddr;
+
+       baddr = (bus_addr_t *)arg;
+       *baddr = segs->ds_addr;
+}
+
+static void
+ahc_build_free_scb_list(struct ahc_softc *ahc)
+{
+       int scbsize;
+       int i;
+
+       scbsize = 32;
+       if ((ahc->flags & AHC_LSCBS_ENABLED) != 0)
+               scbsize = 64;
+
+       for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+               int j;
+
+               ahc_outb(ahc, SCBPTR, i);
+
+               /*
+                * Touch all SCB bytes to avoid parity errors
+                * should one of our debugging routines read
+                * an otherwise uninitiatlized byte.
+                */
+               for (j = 0; j < scbsize; j++)
+                       ahc_outb(ahc, SCB_BASE+j, 0xFF);
+
+               /* Clear the control byte. */
+               ahc_outb(ahc, SCB_CONTROL, 0);
+
+               /* Set the next pointer */
+               if ((ahc->flags & AHC_PAGESCBS) != 0)
+                       ahc_outb(ahc, SCB_NEXT, i+1);
+               else 
+                       ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+
+               /* Make the tag number, SCSIID, and lun invalid */
+               ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+               ahc_outb(ahc, SCB_SCSIID, 0xFF);
+               ahc_outb(ahc, SCB_LUN, 0xFF);
+       }
+
+       /* Make sure that the last SCB terminates the free list */
+       ahc_outb(ahc, SCBPTR, i-1);
+       ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+}
+
+static int
+ahc_init_scbdata(struct ahc_softc *ahc)
+{
+       struct scb_data *scb_data;
+
+       scb_data = ahc->scb_data;
+       SLIST_INIT(&scb_data->free_scbs);
+       SLIST_INIT(&scb_data->sg_maps);
+
+       /* Allocate SCB resources */
+       scb_data->scbarray =
+           (struct scb *)malloc(sizeof(struct scb) * AHC_SCB_MAX_ALLOC,
+                                M_DEVBUF, M_NOWAIT);
+       if (scb_data->scbarray == NULL)
+               return (ENOMEM);
+       memset(scb_data->scbarray, 0, sizeof(struct scb) * AHC_SCB_MAX_ALLOC);
+
+       /* Determine the number of hardware SCBs and initialize them */
+
+       scb_data->maxhscbs = ahc_probe_scbs(ahc);
+       if ((ahc->flags & AHC_PAGESCBS) != 0) {
+               /* SCB 0 heads the free list */
+               ahc_outb(ahc, FREE_SCBH, 0);
+       } else {
+               ahc_outb(ahc, FREE_SCBH, SCB_LIST_NULL);
+       }
+
+       if (ahc->scb_data->maxhscbs == 0) {
+               printf("%s: No SCB space found\n", ahc_name(ahc));
+               return (ENXIO);
+       }
+
+       ahc_build_free_scb_list(ahc);
+
+       /*
+        * Create our DMA tags.  These tags define the kinds of device
+        * accessible memory allocations and memory mappings we will
+        * need to perform during normal operation.
+        *
+        * Unless we need to further restrict the allocation, we rely
+        * on the restrictions of the parent dmat, hence the common
+        * use of MAXADDR and MAXSIZE.
+        */
+
+       /* DMA tag for our hardware scb structures */
+       if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+                              /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+                              /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+                              /*highaddr*/BUS_SPACE_MAXADDR,
+                              /*filter*/NULL, /*filterarg*/NULL,
+                              AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb),
+                              /*nsegments*/1,
+                              /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+                              /*flags*/0, &scb_data->hscb_dmat) != 0) {
+               goto error_exit;
+       }
+
+       scb_data->init_level++;
+
+       /* Allocation for our hscbs */
+       if (ahc_dmamem_alloc(ahc, scb_data->hscb_dmat,
+                            (void **)&scb_data->hscbs,
+                            BUS_DMA_NOWAIT, &scb_data->hscb_dmamap) != 0) {
+               goto error_exit;
+       }
+
+       scb_data->init_level++;
+
+       /* And permanently map them */
+       ahc_dmamap_load(ahc, scb_data->hscb_dmat, scb_data->hscb_dmamap,
+                       scb_data->hscbs,
+                       AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb),
+                       ahc_dmamap_cb, &scb_data->hscb_busaddr, /*flags*/0);
+
+       scb_data->init_level++;
+
+       /* DMA tag for our sense buffers */
+       if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+                              /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+                              /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+                              /*highaddr*/BUS_SPACE_MAXADDR,
+                              /*filter*/NULL, /*filterarg*/NULL,
+                              AHC_SCB_MAX_ALLOC * sizeof(struct scsi_sense_data),
+                              /*nsegments*/1,
+                              /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+                              /*flags*/0, &scb_data->sense_dmat) != 0) {
+               goto error_exit;
+       }
+
+       scb_data->init_level++;
+
+       /* Allocate them */
+       if (ahc_dmamem_alloc(ahc, scb_data->sense_dmat,
+                            (void **)&scb_data->sense,
+                            BUS_DMA_NOWAIT, &scb_data->sense_dmamap) != 0) {
+               goto error_exit;
+       }
+
+       scb_data->init_level++;
+
+       /* And permanently map them */
+       ahc_dmamap_load(ahc, scb_data->sense_dmat, scb_data->sense_dmamap,
+                       scb_data->sense,
+                       AHC_SCB_MAX_ALLOC * sizeof(struct scsi_sense_data),
+                       ahc_dmamap_cb, &scb_data->sense_busaddr, /*flags*/0);
+
+       scb_data->init_level++;
+
+       /* DMA tag for our S/G structures.  We allocate in page sized chunks */
+       if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+                              /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+                              /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+                              /*highaddr*/BUS_SPACE_MAXADDR,
+                              /*filter*/NULL, /*filterarg*/NULL,
+                              PAGE_SIZE, /*nsegments*/1,
+                              /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+                              /*flags*/0, &scb_data->sg_dmat) != 0) {
+               goto error_exit;
+       }
+
+       scb_data->init_level++;
+
+       /* Perform initial CCB allocation */
+       memset(scb_data->hscbs, 0,
+              AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb));
+       ahc_alloc_scbs(ahc);
+
+       if (scb_data->numscbs == 0) {
+               printf("%s: ahc_init_scbdata - "
+                      "Unable to allocate initial scbs\n",
+                      ahc_name(ahc));
+               goto error_exit;
+       }
+
+       /*
+        * Tell the sequencer which SCB will be the next one it receives.
+        */
+       ahc->next_queued_scb = ahc_get_scb(ahc);
+       ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
+
+       /*
+        * Note that we were successfull
+        */
+       return (0); 
+
+error_exit:
+
+       return (ENOMEM);
+}
+
+static void
+ahc_fini_scbdata(struct ahc_softc *ahc)
+{
+       struct scb_data *scb_data;
+
+       scb_data = ahc->scb_data;
+       if (scb_data == NULL)
+               return;
+
+       switch (scb_data->init_level) {
+       default:
+       case 7:
+       {
+               struct sg_map_node *sg_map;
+
+               while ((sg_map = SLIST_FIRST(&scb_data->sg_maps))!= NULL) {
+                       SLIST_REMOVE_HEAD(&scb_data->sg_maps, links);
+                       ahc_dmamap_unload(ahc, scb_data->sg_dmat,
+                                         sg_map->sg_dmamap);
+                       ahc_dmamem_free(ahc, scb_data->sg_dmat,
+                                       sg_map->sg_vaddr,
+                                       sg_map->sg_dmamap);
+                       free(sg_map, M_DEVBUF);
+               }
+               ahc_dma_tag_destroy(ahc, scb_data->sg_dmat);
+       }
+       case 6:
+               ahc_dmamap_unload(ahc, scb_data->sense_dmat,
+                                 scb_data->sense_dmamap);
+       case 5:
+               ahc_dmamem_free(ahc, scb_data->sense_dmat, scb_data->sense,
+                               scb_data->sense_dmamap);
+               ahc_dmamap_destroy(ahc, scb_data->sense_dmat,
+                                  scb_data->sense_dmamap);
+       case 4:
+               ahc_dma_tag_destroy(ahc, scb_data->sense_dmat);
+       case 3:
+               ahc_dmamap_unload(ahc, scb_data->hscb_dmat,
+                                 scb_data->hscb_dmamap);
+       case 2:
+               ahc_dmamem_free(ahc, scb_data->hscb_dmat, scb_data->hscbs,
+                               scb_data->hscb_dmamap);
+               ahc_dmamap_destroy(ahc, scb_data->hscb_dmat,
+                                  scb_data->hscb_dmamap);
+       case 1:
+               ahc_dma_tag_destroy(ahc, scb_data->hscb_dmat);
+               break;
+       case 0:
+               break;
+       }
+       if (scb_data->scbarray != NULL)
+               free(scb_data->scbarray, M_DEVBUF);
+}
+
+void
+ahc_alloc_scbs(struct ahc_softc *ahc)
+{
+       struct scb_data *scb_data;
+       struct scb *next_scb;
+       struct sg_map_node *sg_map;
+       bus_addr_t physaddr;
+       struct ahc_dma_seg *segs;
+       int newcount;
+       int i;
+
+       scb_data = ahc->scb_data;
+       if (scb_data->numscbs >= AHC_SCB_MAX_ALLOC)
+               /* Can't allocate any more */
+               return;
+
+       next_scb = &scb_data->scbarray[scb_data->numscbs];
+
+       sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT);
+
+       if (sg_map == NULL)
+               return;
+
+       /* Allocate S/G space for the next batch of SCBS */
+       if (ahc_dmamem_alloc(ahc, scb_data->sg_dmat,
+                            (void **)&sg_map->sg_vaddr,
+                            BUS_DMA_NOWAIT, &sg_map->sg_dmamap) != 0) {
+               free(sg_map, M_DEVBUF);
+               return;
+       }
+
+       SLIST_INSERT_HEAD(&scb_data->sg_maps, sg_map, links);
+
+       ahc_dmamap_load(ahc, scb_data->sg_dmat, sg_map->sg_dmamap,
+                       sg_map->sg_vaddr, PAGE_SIZE, ahc_dmamap_cb,
+                       &sg_map->sg_physaddr, /*flags*/0);
+
+       segs = sg_map->sg_vaddr;
+       physaddr = sg_map->sg_physaddr;
+
+       newcount = (PAGE_SIZE / (AHC_NSEG * sizeof(struct ahc_dma_seg)));
+       newcount = MIN(newcount, (AHC_SCB_MAX_ALLOC - scb_data->numscbs));
+       for (i = 0; i < newcount; i++) {
+               struct scb_platform_data *pdata;
+#ifndef __linux__
+               int error;
+#endif
+               pdata = (struct scb_platform_data *)malloc(sizeof(*pdata),
+                                                          M_DEVBUF, M_NOWAIT);
+               if (pdata == NULL)
+                       break;
+               next_scb->platform_data = pdata;
+               next_scb->sg_map = sg_map;
+               next_scb->sg_list = segs;
+               /*
+                * The sequencer always starts with the second entry.
+                * The first entry is embedded in the scb.
+                */
+               next_scb->sg_list_phys = physaddr + sizeof(struct ahc_dma_seg);
+               next_scb->ahc_softc = ahc;
+               next_scb->flags = SCB_FREE;
+#ifndef __linux__
+               error = ahc_dmamap_create(ahc, ahc->buffer_dmat, /*flags*/0,
+                                         &next_scb->dmamap);
+               if (error != 0)
+                       break;
+#endif
+               next_scb->hscb = &scb_data->hscbs[scb_data->numscbs];
+               next_scb->hscb->tag = ahc->scb_data->numscbs;
+               SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs,
+                                 next_scb, links.sle);
+               segs += AHC_NSEG;
+               physaddr += (AHC_NSEG * sizeof(struct ahc_dma_seg));
+               next_scb++;
+               ahc->scb_data->numscbs++;
+       }
+}
+
+void
+ahc_controller_info(struct ahc_softc *ahc, char *buf)
+{
+       int len;
+
+       len = sprintf(buf, "%s: ", ahc_chip_names[ahc->chip & AHC_CHIPID_MASK]);
+       buf += len;
+       if ((ahc->features & AHC_TWIN) != 0)
+               len = sprintf(buf, "Twin Channel, A SCSI Id=%d, "
+                             "B SCSI Id=%d, primary %c, ",
+                             ahc->our_id, ahc->our_id_b,
+                             (ahc->flags & AHC_PRIMARY_CHANNEL) + 'A');
+       else {
+               const char *speed;
+               const char *type;
+
+               speed = "";
+               if ((ahc->features & AHC_ULTRA) != 0) {
+                       speed = "Ultra ";
+               } else if ((ahc->features & AHC_DT) != 0) {
+                       speed = "Ultra160 ";
+               } else if ((ahc->features & AHC_ULTRA2) != 0) {
+                       speed = "Ultra2 ";
+               }
+               if ((ahc->features & AHC_WIDE) != 0) {
+                       type = "Wide";
+               } else {
+                       type = "Single";
+               }
+               len = sprintf(buf, "%s%s Channel %c, SCSI Id=%d, ",
+                             speed, type, ahc->channel, ahc->our_id);
+       }
+       buf += len;
+
+       if ((ahc->flags & AHC_PAGESCBS) != 0)
+               sprintf(buf, "%d/%d SCBs",
+                       ahc->scb_data->maxhscbs, AHC_MAX_QUEUE);
+       else
+               sprintf(buf, "%d SCBs", ahc->scb_data->maxhscbs);
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+ahc_init(struct ahc_softc *ahc)
+{
+       int      max_targ;
+       int      i;
+       int      term;
+       u_int    scsi_conf;
+       u_int    scsiseq_template;
+       u_int    ultraenb;
+       u_int    discenable;
+       u_int    tagenable;
+       size_t   driver_data_size;
+       uint32_t physaddr;
+
+#ifdef AHC_DEBUG_SEQUENCER
+       ahc->flags |= AHC_SEQUENCER_DEBUG;
+#endif
+
+#ifdef AHC_PRINT_SRAM
+       printf("Scratch Ram:");
+       for (i = 0x20; i < 0x5f; i++) {
+               if (((i % 8) == 0) && (i != 0)) {
+                       printf ("\n              ");
+               }
+               printf (" 0x%x", ahc_inb(ahc, i));
+       }
+       if ((ahc->features & AHC_MORE_SRAM) != 0) {
+               for (i = 0x70; i < 0x7f; i++) {
+                       if (((i % 8) == 0) && (i != 0)) {
+                               printf ("\n              ");
+                       }
+                       printf (" 0x%x", ahc_inb(ahc, i));
+               }
+       }
+       printf ("\n");
+       /*
+        * Reading uninitialized scratch ram may
+        * generate parity errors.
+        */
+       ahc_outb(ahc, CLRINT, CLRPARERR);
+       ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+#endif
+       max_targ = 15;
+
+       /*
+        * Assume we have a board at this stage and it has been reset.
+        */
+       if ((ahc->flags & AHC_USEDEFAULTS) != 0)
+               ahc->our_id = ahc->our_id_b = 7;
+       
+       /*
+        * Default to allowing initiator operations.
+        */
+       ahc->flags |= AHC_INITIATORROLE;
+
+       /*
+        * Only allow target mode features if this unit has them enabled.
+        */
+       if ((AHC_TMODE_ENABLE & (0x1 << ahc->unit)) == 0)
+               ahc->features &= ~AHC_TARGETMODE;
+
+#ifndef __linux__
+       /* DMA tag for mapping buffers into device visible space. */
+       if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+                              /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+                              /*lowaddr*/BUS_SPACE_MAXADDR,
+                              /*highaddr*/BUS_SPACE_MAXADDR,
+                              /*filter*/NULL, /*filterarg*/NULL,
+                              /*maxsize*/MAXBSIZE, /*nsegments*/AHC_NSEG,
+                              /*maxsegsz*/AHC_MAXTRANSFER_SIZE,
+                              /*flags*/BUS_DMA_ALLOCNOW,
+                              &ahc->buffer_dmat) != 0) {
+               return (ENOMEM);
+       }
+#endif
+
+       ahc->init_level++;
+
+       /*
+        * DMA tag for our command fifos and other data in system memory
+        * the card's sequencer must be able to access.  For initiator
+        * roles, we need to allocate space for the the qinfifo and qoutfifo.
+        * The qinfifo and qoutfifo are composed of 256 1 byte elements. 
+        * When providing for the target mode role, we must additionally
+        * provide space for the incoming target command fifo and an extra
+        * byte to deal with a dma bug in some chip versions.
+        */
+       driver_data_size = 2 * 256 * sizeof(uint8_t);
+       if ((ahc->features & AHC_TARGETMODE) != 0)
+               driver_data_size += AHC_TMODE_CMDS * sizeof(struct target_cmd)
+                                + /*DMA WideOdd Bug Buffer*/1;
+       if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+                              /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+                              /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+                              /*highaddr*/BUS_SPACE_MAXADDR,
+                              /*filter*/NULL, /*filterarg*/NULL,
+                              driver_data_size,
+                              /*nsegments*/1,
+                              /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+                              /*flags*/0, &ahc->shared_data_dmat) != 0) {
+               return (ENOMEM);
+       }
+
+       ahc->init_level++;
+
+       /* Allocation of driver data */
+       if (ahc_dmamem_alloc(ahc, ahc->shared_data_dmat,
+                            (void **)&ahc->qoutfifo,
+                            BUS_DMA_NOWAIT, &ahc->shared_data_dmamap) != 0) {
+               return (ENOMEM);
+       }
+
+       ahc->init_level++;
+
+       /* And permanently map it in */
+       ahc_dmamap_load(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
+                       ahc->qoutfifo, driver_data_size, ahc_dmamap_cb,
+                       &ahc->shared_data_busaddr, /*flags*/0);
+
+       if ((ahc->features & AHC_TARGETMODE) != 0) {
+               ahc->targetcmds = (struct target_cmd *)ahc->qoutfifo;
+               ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[AHC_TMODE_CMDS];
+               ahc->dma_bug_buf = ahc->shared_data_busaddr
+                                + driver_data_size - 1;
+               /* All target command blocks start out invalid. */
+               for (i = 0; i < AHC_TMODE_CMDS; i++)
+                       ahc->targetcmds[i].cmd_valid = 0;
+               ahc_sync_tqinfifo(ahc, BUS_DMASYNC_PREREAD);
+               ahc->tqinfifonext = 1;
+               ahc_outb(ahc, KERNEL_TQINPOS, ahc->tqinfifonext - 1);
+               ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
+               ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[256];
+       }
+       ahc->qinfifo = &ahc->qoutfifo[256];
+
+       ahc->init_level++;
+
+       /* Allocate SCB data now that buffer_dmat is initialized */
+       if (ahc->scb_data->maxhscbs == 0)
+               if (ahc_init_scbdata(ahc) != 0)
+                       return (ENOMEM);
+
+       /*
+        * Allocate a tstate to house information for our
+        * initiator presence on the bus as well as the user
+        * data for any target mode initiator.
+        */
+       if (ahc_alloc_tstate(ahc, ahc->our_id, 'A') == NULL) {
+               printf("%s: unable to allocate ahc_tmode_tstate.  "
+                      "Failing attach\n", ahc_name(ahc));
+               return (ENOMEM);
+       }
+
+       if ((ahc->features & AHC_TWIN) != 0) {
+               if (ahc_alloc_tstate(ahc, ahc->our_id_b, 'B') == NULL) {
+                       printf("%s: unable to allocate ahc_tmode_tstate.  "
+                              "Failing attach\n", ahc_name(ahc));
+                       return (ENOMEM);
+               }
+       }
+
+       ahc_outb(ahc, SEQ_FLAGS, 0);
+       ahc_outb(ahc, SEQ_FLAGS2, 0);
+
+       if (ahc->scb_data->maxhscbs < AHC_SCB_MAX_ALLOC) {
+               ahc->flags |= AHC_PAGESCBS;
+       } else {
+               ahc->flags &= ~AHC_PAGESCBS;
+       }
+
+#ifdef AHC_DEBUG
+       if (ahc_debug & AHC_SHOWMISC) {
+               printf("%s: hardware scb %d bytes; kernel scb %d bytes; "
+                      "ahc_dma %d bytes\n",
+                       ahc_name(ahc),
+                       sizeof(struct hardware_scb),
+                       sizeof(struct scb),
+                       sizeof(struct ahc_dma_seg));
+       }
+#endif /* AHC_DEBUG */
+
+       /* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels*/
+       if (ahc->features & AHC_TWIN) {
+
+               /*
+                * The device is gated to channel B after a chip reset,
+                * so set those values first
+                */
+               ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+               term = (ahc->flags & AHC_TERM_ENB_B) != 0 ? STPWEN : 0;
+               ahc_outb(ahc, SCSIID, ahc->our_id_b);
+               scsi_conf = ahc_inb(ahc, SCSICONF + 1);
+               ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+                                       |term|ahc->seltime_b|ENSTIMER|ACTNEGEN);
+               if ((ahc->features & AHC_ULTRA2) != 0)
+                       ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
+               ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+               ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
+
+               if ((scsi_conf & RESET_SCSI) != 0
+                && (ahc->flags & AHC_INITIATORROLE) != 0)
+                       ahc->flags |= AHC_RESET_BUS_B;
+
+               /* Select Channel A */
+               ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+       }
+       term = (ahc->flags & AHC_TERM_ENB_A) != 0 ? STPWEN : 0;
+       if ((ahc->features & AHC_ULTRA2) != 0)
+               ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
+       else
+               ahc_outb(ahc, SCSIID, ahc->our_id);
+       scsi_conf = ahc_inb(ahc, SCSICONF);
+       ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+                               |term|ahc->seltime
+                               |ENSTIMER|ACTNEGEN);
+       if ((ahc->features & AHC_ULTRA2) != 0)
+               ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
+       ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+       ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
+
+       if ((scsi_conf & RESET_SCSI) != 0
+        && (ahc->flags & AHC_INITIATORROLE) != 0)
+               ahc->flags |= AHC_RESET_BUS_A;
+
+       /*
+        * Look at the information that board initialization or
+        * the board bios has left us.
+        */
+       ultraenb = 0;   
+       tagenable = ALL_TARGETS_MASK;
+
+       /* Grab the disconnection disable table and invert it for our needs */
+       if ((ahc->flags & AHC_USEDEFAULTS) != 0) {
+               printf("%s: Host Adapter Bios disabled.  Using default SCSI "
+                       "device parameters\n", ahc_name(ahc));
+               ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B|
+                             AHC_TERM_ENB_A|AHC_TERM_ENB_B;
+               discenable = ALL_TARGETS_MASK;
+               if ((ahc->features & AHC_ULTRA) != 0)
+                       ultraenb = ALL_TARGETS_MASK;
+       } else {
+               discenable = ~((ahc_inb(ahc, DISC_DSB + 1) << 8)
+                          | ahc_inb(ahc, DISC_DSB));
+               if ((ahc->features & (AHC_ULTRA|AHC_ULTRA2)) != 0)
+                       ultraenb = (ahc_inb(ahc, ULTRA_ENB + 1) << 8)
+                                     | ahc_inb(ahc, ULTRA_ENB);
+       }
+
+       if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
+               max_targ = 7;
+
+       for (i = 0; i <= max_targ; i++) {
+               struct ahc_initiator_tinfo *tinfo;
+               struct ahc_tmode_tstate *tstate;
+               u_int our_id;
+               u_int target_id;
+               char channel;
+
+               channel = 'A';
+               our_id = ahc->our_id;
+               target_id = i;
+               if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+                       channel = 'B';
+                       our_id = ahc->our_id_b;
+                       target_id = i % 8;
+               }
+               tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+                                           target_id, &tstate);
+               /* Default to async narrow across the board */
+               memset(tinfo, 0, sizeof(*tinfo));
+               if (ahc->flags & AHC_USEDEFAULTS) {
+                       if ((ahc->features & AHC_WIDE) != 0)
+                               tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+
+                       /*
+                        * These will be truncated when we determine the
+                        * connection type we have with the target.
+                        */
+                       tinfo->user.period = ahc_syncrates->period;
+                       tinfo->user.offset = ~0;
+               } else {
+                       u_int scsirate;
+                       uint16_t mask;
+
+                       /* Take the settings leftover in scratch RAM. */
+                       scsirate = ahc_inb(ahc, TARG_SCSIRATE + i);
+                       mask = (0x01 << i);
+                       if ((ahc->features & AHC_ULTRA2) != 0) {
+                               u_int offset;
+                               u_int maxsync;
+
+                               if ((scsirate & SOFS) == 0x0F) {
+                                       /*
+                                        * Haven't negotiated yet,
+                                        * so the format is different.
+                                        */
+                                       scsirate = (scsirate & SXFR) >> 4
+                                                | (ultraenb & mask)
+                                                 ? 0x08 : 0x0
+                                                | (scsirate & WIDEXFER);
+                                       offset = MAX_OFFSET_ULTRA2;
+                               } else
+                                       offset = ahc_inb(ahc, TARG_OFFSET + i);
+                               if ((scsirate & ~WIDEXFER) == 0 && offset != 0)
+                                       /* Set to the lowest sync rate, 5MHz */
+                                       scsirate |= 0x1c;
+                               maxsync = AHC_SYNCRATE_ULTRA2;
+                               if ((ahc->features & AHC_DT) != 0)
+                                       maxsync = AHC_SYNCRATE_DT;
+                               tinfo->user.period =
+                                   ahc_find_period(ahc, scsirate, maxsync);
+                               if (offset == 0)
+                                       tinfo->user.period = 0;
+                               else
+                                       tinfo->user.offset = ~0;
+                               if ((scsirate & SXFR_ULTRA2) <= 8/*10MHz*/
+                                && (ahc->features & AHC_DT) != 0)
+                                       tinfo->user.ppr_options =
+                                           MSG_EXT_PPR_DT_REQ;
+                       } else if ((scsirate & SOFS) != 0) {
+                               if ((scsirate & SXFR) == 0x40
+                                && (ultraenb & mask) != 0) {
+                                       /* Treat 10MHz as a non-ultra speed */
+                                       scsirate &= ~SXFR;
+                                       ultraenb &= ~mask;
+                               }
+                               tinfo->user.period = 
+                                   ahc_find_period(ahc, scsirate,
+                                                   (ultraenb & mask)
+                                                  ? AHC_SYNCRATE_ULTRA
+                                                  : AHC_SYNCRATE_FAST);
+                               if (tinfo->user.period != 0)
+                                       tinfo->user.offset = ~0;
+                       }
+                       if (tinfo->user.period == 0)
+                               tinfo->user.offset = 0;
+                       if ((scsirate & WIDEXFER) != 0
+                        && (ahc->features & AHC_WIDE) != 0)
+                               tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+                       tinfo->user.protocol_version = 4;
+                       if ((ahc->features & AHC_DT) != 0)
+                               tinfo->user.transport_version = 3;
+                       else
+                               tinfo->user.transport_version = 2;
+                       tinfo->goal.protocol_version = 2;
+                       tinfo->goal.transport_version = 2;
+                       tinfo->curr.protocol_version = 2;
+                       tinfo->curr.transport_version = 2;
+               }
+               tstate->ultraenb = ultraenb;
+       }
+       ahc->user_discenable = discenable;
+       ahc->user_tagenable = tagenable;
+
+       /* There are no untagged SCBs active yet. */
+       for (i = 0; i < 16; i++) {
+               ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, 0));
+               if ((ahc->flags & AHC_SCB_BTT) != 0) {
+                       int lun;
+
+                       /*
+                        * The SCB based BTT allows an entry per
+                        * target and lun pair.
+                        */
+                       for (lun = 1; lun < AHC_NUM_LUNS; lun++)
+                               ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, lun));
+               }
+       }
+
+       /* All of our queues are empty */
+       for (i = 0; i < 256; i++)
+               ahc->qoutfifo[i] = SCB_LIST_NULL;
+       ahc_sync_qoutfifo(ahc, BUS_DMASYNC_PREREAD);
+
+       for (i = 0; i < 256; i++)
+               ahc->qinfifo[i] = SCB_LIST_NULL;
+
+       if ((ahc->features & AHC_MULTI_TID) != 0) {
+               ahc_outb(ahc, TARGID, 0);
+               ahc_outb(ahc, TARGID + 1, 0);
+       }
+
+       /*
+        * Tell the sequencer where it can find our arrays in memory.
+        */
+       physaddr = ahc->scb_data->hscb_busaddr;
+       ahc_outb(ahc, HSCB_ADDR, physaddr & 0xFF);
+       ahc_outb(ahc, HSCB_ADDR + 1, (physaddr >> 8) & 0xFF);
+       ahc_outb(ahc, HSCB_ADDR + 2, (physaddr >> 16) & 0xFF);
+       ahc_outb(ahc, HSCB_ADDR + 3, (physaddr >> 24) & 0xFF);
+
+       physaddr = ahc->shared_data_busaddr;
+       ahc_outb(ahc, SHARED_DATA_ADDR, physaddr & 0xFF);
+       ahc_outb(ahc, SHARED_DATA_ADDR + 1, (physaddr >> 8) & 0xFF);
+       ahc_outb(ahc, SHARED_DATA_ADDR + 2, (physaddr >> 16) & 0xFF);
+       ahc_outb(ahc, SHARED_DATA_ADDR + 3, (physaddr >> 24) & 0xFF);
+
+       /*
+        * Initialize the group code to command length table.
+        * This overrides the values in TARG_SCSIRATE, so only
+        * setup the table after we have processed that information.
+        */
+       ahc_outb(ahc, CMDSIZE_TABLE, 5);
+       ahc_outb(ahc, CMDSIZE_TABLE + 1, 9);
+       ahc_outb(ahc, CMDSIZE_TABLE + 2, 9);
+       ahc_outb(ahc, CMDSIZE_TABLE + 3, 0);
+       ahc_outb(ahc, CMDSIZE_TABLE + 4, 15);
+       ahc_outb(ahc, CMDSIZE_TABLE + 5, 11);
+       ahc_outb(ahc, CMDSIZE_TABLE + 6, 0);
+       ahc_outb(ahc, CMDSIZE_TABLE + 7, 0);
+               
+       /* Tell the sequencer of our initial queue positions */
+       ahc_outb(ahc, KERNEL_QINPOS, 0);
+       ahc_outb(ahc, QINPOS, 0);
+       ahc_outb(ahc, QOUTPOS, 0);
+
+       /*
+        * Use the built in queue management registers
+        * if they are available.
+        */
+       if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+               ahc_outb(ahc, QOFF_CTLSTA, SCB_QSIZE_256);
+               ahc_outb(ahc, SDSCB_QOFF, 0);
+               ahc_outb(ahc, SNSCB_QOFF, 0);
+               ahc_outb(ahc, HNSCB_QOFF, 0);
+       }
+
+
+       /* We don't have any waiting selections */
+       ahc_outb(ahc, WAITING_SCBH, SCB_LIST_NULL);
+
+       /* Our disconnection list is empty too */
+       ahc_outb(ahc, DISCONNECTED_SCBH, SCB_LIST_NULL);
+
+       /* Message out buffer starts empty */
+       ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+
+       /*
+        * Setup the allowed SCSI Sequences based on operational mode.
+        * If we are a target, we'll enalbe select in operations once
+        * we've had a lun enabled.
+        */
+       scsiseq_template = ENSELO|ENAUTOATNO|ENAUTOATNP;
+       if ((ahc->flags & AHC_INITIATORROLE) != 0)
+               scsiseq_template |= ENRSELI;
+       ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq_template);
+
+       /*
+        * Load the Sequencer program and Enable the adapter
+        * in "fast" mode.
+        */
+       if (bootverbose)
+               printf("%s: Downloading Sequencer Program...",
+                      ahc_name(ahc));
+
+       ahc_loadseq(ahc);
+
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               int wait;
+
+               /*
+                * Wait for up to 500ms for our transceivers
+                * to settle.  If the adapter does not have
+                * a cable attached, the tranceivers may
+                * never settle, so don't complain if we
+                * fail here.
+                */
+               ahc_pause(ahc);
+               for (wait = 5000;
+                    (ahc_inb(ahc, SBLKCTL) & (ENAB40|ENAB20)) == 0 && wait;
+                    wait--)
+                       ahc_delay(100);
+               ahc_unpause(ahc);
+       }
+       return (0);
+}
+
+void
+ahc_intr_enable(struct ahc_softc *ahc, int enable)
+{
+       u_int hcntrl;
+
+       hcntrl = ahc_inb(ahc, HCNTRL);
+       hcntrl &= ~INTEN;
+       ahc->pause &= ~INTEN;
+       ahc->unpause &= ~INTEN;
+       if (enable) {
+               hcntrl |= INTEN;
+               ahc->pause |= INTEN;
+               ahc->unpause |= INTEN;
+       }
+       ahc_outb(ahc, HCNTRL, hcntrl);
+}
+
+/*
+ * Ensure that the card is paused in a location
+ * outside of all critical sections and that all
+ * pending work is completed prior to returning.
+ * This routine should only be called from outside
+ * an interrupt context.
+ */
+void
+ahc_pause_and_flushwork(struct ahc_softc *ahc)
+{
+       int intstat;
+       int maxloops;
+
+       maxloops = 1000;
+       ahc->flags |= AHC_ALL_INTERRUPTS;
+       intstat = 0;
+       do {
+               ahc_intr(ahc);
+               ahc_pause(ahc);
+               ahc_clear_critical_section(ahc);
+               if (intstat == 0xFF && (ahc->features & AHC_REMOVABLE) != 0)
+                       break;
+               maxloops--;
+       } while (((intstat = ahc_inb(ahc, INTSTAT)) & INT_PEND) && --maxloops);
+       if (maxloops == 0) {
+               printf("Infinite interrupt loop, INTSTAT = %x",
+                     ahc_inb(ahc, INTSTAT));
+       }
+       ahc_platform_flushwork(ahc);
+       ahc->flags &= ~AHC_ALL_INTERRUPTS;
+}
+
+int
+ahc_suspend(struct ahc_softc *ahc)
+{
+       uint8_t *ptr;
+       int      i;
+
+       ahc_pause_and_flushwork(ahc);
+
+       if (LIST_FIRST(&ahc->pending_scbs) != NULL)
+               return (EBUSY);
+
+#if AHC_TARGET_MODE
+       /*
+        * XXX What about ATIOs that have not yet been serviced?
+        * Perhaps we should just refuse to be suspended if we
+        * are acting in a target role.
+        */
+       if (ahc->pending_device != NULL)
+               return (EBUSY);
+#endif
+
+       /* Save volatile registers */
+       if ((ahc->features & AHC_TWIN) != 0) {
+               ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+               ahc->suspend_state.channel[1].scsiseq = ahc_inb(ahc, SCSISEQ);
+               ahc->suspend_state.channel[1].sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+               ahc->suspend_state.channel[1].sxfrctl1 = ahc_inb(ahc, SXFRCTL1);
+               ahc->suspend_state.channel[1].simode0 = ahc_inb(ahc, SIMODE0);
+               ahc->suspend_state.channel[1].simode1 = ahc_inb(ahc, SIMODE1);
+               ahc->suspend_state.channel[1].seltimer = ahc_inb(ahc, SELTIMER);
+               ahc->suspend_state.channel[1].seqctl = ahc_inb(ahc, SEQCTL);
+               ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+       }
+       ahc->suspend_state.channel[0].scsiseq = ahc_inb(ahc, SCSISEQ);
+       ahc->suspend_state.channel[0].sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+       ahc->suspend_state.channel[0].sxfrctl1 = ahc_inb(ahc, SXFRCTL1);
+       ahc->suspend_state.channel[0].simode0 = ahc_inb(ahc, SIMODE0);
+       ahc->suspend_state.channel[0].simode1 = ahc_inb(ahc, SIMODE1);
+       ahc->suspend_state.channel[0].seltimer = ahc_inb(ahc, SELTIMER);
+       ahc->suspend_state.channel[0].seqctl = ahc_inb(ahc, SEQCTL);
+
+       if ((ahc->chip & AHC_PCI) != 0) {
+               ahc->suspend_state.dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+               ahc->suspend_state.dspcistatus = ahc_inb(ahc, DSPCISTATUS);
+       }
+
+       if ((ahc->features & AHC_DT) != 0) {
+               u_int sfunct;
+
+               sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+               ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+               ahc->suspend_state.optionmode = ahc_inb(ahc, OPTIONMODE);
+               ahc_outb(ahc, SFUNCT, sfunct);
+               ahc->suspend_state.crccontrol1 = ahc_inb(ahc, CRCCONTROL1);
+       }
+
+       if ((ahc->features & AHC_MULTI_FUNC) != 0)
+               ahc->suspend_state.scbbaddr = ahc_inb(ahc, SCBBADDR);
+
+       if ((ahc->features & AHC_ULTRA2) != 0)
+               ahc->suspend_state.dff_thrsh = ahc_inb(ahc, DFF_THRSH);
+
+       ptr = ahc->suspend_state.scratch_ram;
+       for (i = 0; i < 64; i++)
+               *ptr++ = ahc_inb(ahc, SRAM_BASE + i);
+
+       if ((ahc->features & AHC_MORE_SRAM) != 0) {
+               for (i = 0; i < 16; i++)
+                       *ptr++ = ahc_inb(ahc, TARG_OFFSET + i);
+       }
+
+       ptr = ahc->suspend_state.btt;
+       if ((ahc->flags & AHC_SCB_BTT) != 0) {
+               for (i = 0;i < AHC_NUM_TARGETS; i++) {
+                       int j;
+
+                       for (j = 0;j < AHC_NUM_LUNS; j++) {
+                               u_int tcl;
+
+                               tcl = BUILD_TCL(i << 4, j);
+                               *ptr = ahc_index_busy_tcl(ahc, tcl);
+                       }
+               }
+       }
+       ahc_shutdown(ahc);
+       return (0);
+}
+
+int
+ahc_resume(struct ahc_softc *ahc)
+{
+       uint8_t *ptr;
+       int      i;
+
+       ahc_reset(ahc);
+
+       ahc_build_free_scb_list(ahc);
+
+       /* Restore volatile registers */
+       if ((ahc->features & AHC_TWIN) != 0) {
+               ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+               ahc_outb(ahc, SCSIID, ahc->our_id);
+               ahc_outb(ahc, SCSISEQ, ahc->suspend_state.channel[1].scsiseq);
+               ahc_outb(ahc, SXFRCTL0, ahc->suspend_state.channel[1].sxfrctl0);
+               ahc_outb(ahc, SXFRCTL1, ahc->suspend_state.channel[1].sxfrctl1);
+               ahc_outb(ahc, SIMODE0, ahc->suspend_state.channel[1].simode0);
+               ahc_outb(ahc, SIMODE1, ahc->suspend_state.channel[1].simode1);
+               ahc_outb(ahc, SELTIMER, ahc->suspend_state.channel[1].seltimer);
+               ahc_outb(ahc, SEQCTL, ahc->suspend_state.channel[1].seqctl);
+               ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+       }
+       ahc_outb(ahc, SCSISEQ, ahc->suspend_state.channel[0].scsiseq);
+       ahc_outb(ahc, SXFRCTL0, ahc->suspend_state.channel[0].sxfrctl0);
+       ahc_outb(ahc, SXFRCTL1, ahc->suspend_state.channel[0].sxfrctl1);
+       ahc_outb(ahc, SIMODE0, ahc->suspend_state.channel[0].simode0);
+       ahc_outb(ahc, SIMODE1, ahc->suspend_state.channel[0].simode1);
+       ahc_outb(ahc, SELTIMER, ahc->suspend_state.channel[0].seltimer);
+       ahc_outb(ahc, SEQCTL, ahc->suspend_state.channel[0].seqctl);
+       if ((ahc->features & AHC_ULTRA2) != 0)
+               ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
+       else
+               ahc_outb(ahc, SCSIID, ahc->our_id);
+
+       if ((ahc->chip & AHC_PCI) != 0) {
+               ahc_outb(ahc, DSCOMMAND0, ahc->suspend_state.dscommand0);
+               ahc_outb(ahc, DSPCISTATUS, ahc->suspend_state.dspcistatus);
+       }
+
+       if ((ahc->features & AHC_DT) != 0) {
+               u_int sfunct;
+
+               sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+               ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+               ahc_outb(ahc, OPTIONMODE, ahc->suspend_state.optionmode);
+               ahc_outb(ahc, SFUNCT, sfunct);
+               ahc_outb(ahc, CRCCONTROL1, ahc->suspend_state.crccontrol1);
+       }
+
+       if ((ahc->features & AHC_MULTI_FUNC) != 0)
+               ahc_outb(ahc, SCBBADDR, ahc->suspend_state.scbbaddr);
+
+       if ((ahc->features & AHC_ULTRA2) != 0)
+               ahc_outb(ahc, DFF_THRSH, ahc->suspend_state.dff_thrsh);
+
+       ptr = ahc->suspend_state.scratch_ram;
+       for (i = 0; i < 64; i++)
+               ahc_outb(ahc, SRAM_BASE + i, *ptr++);
+
+       if ((ahc->features & AHC_MORE_SRAM) != 0) {
+               for (i = 0; i < 16; i++)
+                       ahc_outb(ahc, TARG_OFFSET + i, *ptr++);
+       }
+
+       ptr = ahc->suspend_state.btt;
+       if ((ahc->flags & AHC_SCB_BTT) != 0) {
+               for (i = 0;i < AHC_NUM_TARGETS; i++) {
+                       int j;
+
+                       for (j = 0;j < AHC_NUM_LUNS; j++) {
+                               u_int tcl;
+
+                               tcl = BUILD_TCL(i << 4, j);
+                               ahc_busy_tcl(ahc, tcl, *ptr);
+                       }
+               }
+       }
+       return (0);
+}
+
+/************************** Busy Target Table *********************************/
+/*
+ * Return the untagged transaction id for a given target/channel lun.
+ * Optionally, clear the entry.
+ */
+u_int
+ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl)
+{
+       u_int scbid;
+       u_int target_offset;
+
+       if ((ahc->flags & AHC_SCB_BTT) != 0) {
+               u_int saved_scbptr;
+               
+               saved_scbptr = ahc_inb(ahc, SCBPTR);
+               ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+               scbid = ahc_inb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl));
+               ahc_outb(ahc, SCBPTR, saved_scbptr);
+       } else {
+               target_offset = TCL_TARGET_OFFSET(tcl);
+               scbid = ahc_inb(ahc, BUSY_TARGETS + target_offset);
+       }
+
+       return (scbid);
+}
+
+void
+ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl)
+{
+       u_int target_offset;
+
+       if ((ahc->flags & AHC_SCB_BTT) != 0) {
+               u_int saved_scbptr;
+               
+               saved_scbptr = ahc_inb(ahc, SCBPTR);
+               ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+               ahc_outb(ahc, SCB_64_BTT+TCL_TARGET_OFFSET(tcl), SCB_LIST_NULL);
+               ahc_outb(ahc, SCBPTR, saved_scbptr);
+       } else {
+               target_offset = TCL_TARGET_OFFSET(tcl);
+               ahc_outb(ahc, BUSY_TARGETS + target_offset, SCB_LIST_NULL);
+       }
+}
+
+void
+ahc_busy_tcl(struct ahc_softc *ahc, u_int tcl, u_int scbid)
+{
+       u_int target_offset;
+
+       if ((ahc->flags & AHC_SCB_BTT) != 0) {
+               u_int saved_scbptr;
+               
+               saved_scbptr = ahc_inb(ahc, SCBPTR);
+               ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+               ahc_outb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl), scbid);
+               ahc_outb(ahc, SCBPTR, saved_scbptr);
+       } else {
+               target_offset = TCL_TARGET_OFFSET(tcl);
+               ahc_outb(ahc, BUSY_TARGETS + target_offset, scbid);
+       }
+}
+
+/************************** SCB and SCB queue management **********************/
+int
+ahc_match_scb(struct ahc_softc *ahc, struct scb *scb, int target,
+             char channel, int lun, u_int tag, role_t role)
+{
+       int targ = SCB_GET_TARGET(ahc, scb);
+       char chan = SCB_GET_CHANNEL(ahc, scb);
+       int slun = SCB_GET_LUN(scb);
+       int match;
+
+       match = ((chan == channel) || (channel == ALL_CHANNELS));
+       if (match != 0)
+               match = ((targ == target) || (target == CAM_TARGET_WILDCARD));
+       if (match != 0)
+               match = ((lun == slun) || (lun == CAM_LUN_WILDCARD));
+       if (match != 0) {
+#if AHC_TARGET_MODE
+               int group;
+
+               group = XPT_FC_GROUP(scb->io_ctx->ccb_h.func_code);
+               if (role == ROLE_INITIATOR) {
+                       match = (group != XPT_FC_GROUP_TMODE)
+                             && ((tag == scb->hscb->tag)
+                              || (tag == SCB_LIST_NULL));
+               } else if (role == ROLE_TARGET) {
+                       match = (group == XPT_FC_GROUP_TMODE)
+                             && ((tag == scb->io_ctx->csio.tag_id)
+                              || (tag == SCB_LIST_NULL));
+               }
+#else /* !AHC_TARGET_MODE */
+               match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+#endif /* AHC_TARGET_MODE */
+       }
+
+       return match;
+}
+
+void
+ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
+{
+       int     target;
+       char    channel;
+       int     lun;
+
+       target = SCB_GET_TARGET(ahc, scb);
+       lun = SCB_GET_LUN(scb);
+       channel = SCB_GET_CHANNEL(ahc, scb);
+       
+       ahc_search_qinfifo(ahc, target, channel, lun,
+                          /*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
+                          CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+       ahc_platform_freeze_devq(ahc, scb);
+}
+
+void
+ahc_qinfifo_requeue_tail(struct ahc_softc *ahc, struct scb *scb)
+{
+       struct scb *prev_scb;
+
+       prev_scb = NULL;
+       if (ahc_qinfifo_count(ahc) != 0) {
+               u_int prev_tag;
+               uint8_t prev_pos;
+
+               prev_pos = ahc->qinfifonext - 1;
+               prev_tag = ahc->qinfifo[prev_pos];
+               prev_scb = ahc_lookup_scb(ahc, prev_tag);
+       }
+       ahc_qinfifo_requeue(ahc, prev_scb, scb);
+       if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+               ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+       } else {
+               ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+       }
+}
+
+static void
+ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb,
+                   struct scb *scb)
+{
+       if (prev_scb == NULL) {
+               ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
+       } else {
+               prev_scb->hscb->next = scb->hscb->tag;
+               ahc_sync_scb(ahc, prev_scb, 
+                            BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+       }
+       ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+       scb->hscb->next = ahc->next_queued_scb->hscb->tag;
+       ahc_sync_scb(ahc, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+}
+
+static int
+ahc_qinfifo_count(struct ahc_softc *ahc)
+{
+       u_int8_t qinpos;
+       u_int8_t diff;
+
+       if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+               qinpos = ahc_inb(ahc, SNSCB_QOFF);
+               ahc_outb(ahc, SNSCB_QOFF, qinpos);
+       } else
+               qinpos = ahc_inb(ahc, QINPOS);
+       diff = ahc->qinfifonext - qinpos;
+       return (diff);
+}
+
+int
+ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
+                  int lun, u_int tag, role_t role, uint32_t status,
+                  ahc_search_action action)
+{
+       struct  scb *scb;
+       struct  scb *prev_scb;
+       uint8_t qinstart;
+       uint8_t qinpos;
+       uint8_t qintail;
+       uint8_t next;
+       uint8_t prev;
+       uint8_t curscbptr;
+       int     found;
+       int     have_qregs;
+
+       qintail = ahc->qinfifonext;
+       have_qregs = (ahc->features & AHC_QUEUE_REGS) != 0;
+       if (have_qregs) {
+               qinstart = ahc_inb(ahc, SNSCB_QOFF);
+               ahc_outb(ahc, SNSCB_QOFF, qinstart);
+       } else
+               qinstart = ahc_inb(ahc, QINPOS);
+       qinpos = qinstart;
+       found = 0;
+       prev_scb = NULL;
+
+       if (action == SEARCH_COMPLETE) {
+               /*
+                * Don't attempt to run any queued untagged transactions
+                * until we are done with the abort process.
+                */
+               ahc_freeze_untagged_queues(ahc);
+       }
+
+       /*
+        * Start with an empty queue.  Entries that are not chosen
+        * for removal will be re-added to the queue as we go.
+        */
+       ahc->qinfifonext = qinpos;
+       ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
+
+       while (qinpos != qintail) {
+               scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinpos]);
+               if (scb == NULL) {
+                       printf("qinpos = %d, SCB index = %d\n",
+                               qinpos, ahc->qinfifo[qinpos]);
+                       panic("Loop 1\n");
+               }
+
+               if (ahc_match_scb(ahc, scb, target, channel, lun, tag, role)) {
+                       /*
+                        * We found an scb that needs to be acted on.
+                        */
+                       found++;
+                       switch (action) {
+                       case SEARCH_COMPLETE:
+                       {
+                               cam_status ostat;
+                               cam_status cstat;
+
+                               ostat = ahc_get_transaction_status(scb);
+                               if (ostat == CAM_REQ_INPROG)
+                                       ahc_set_transaction_status(scb, status);
+                               cstat = ahc_get_transaction_status(scb);
+                               if (cstat != CAM_REQ_CMP)
+                                       ahc_freeze_scb(scb);
+                               if ((scb->flags & SCB_ACTIVE) == 0)
+                                       printf("Inactive SCB in qinfifo\n");
+                               ahc_done(ahc, scb);
+
+                               /* FALLTHROUGH */
+                       }
+                       case SEARCH_REMOVE:
+                               break;
+                       case SEARCH_COUNT:
+                               ahc_qinfifo_requeue(ahc, prev_scb, scb);
+                               prev_scb = scb;
+                               break;
+                       }
+               } else {
+                       ahc_qinfifo_requeue(ahc, prev_scb, scb);
+                       prev_scb = scb;
+               }
+               qinpos++;
+       }
+
+       if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+               ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+       } else {
+               ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+       }
+
+       if (action != SEARCH_COUNT
+        && (found != 0)
+        && (qinstart != ahc->qinfifonext)) {
+               /*
+                * The sequencer may be in the process of dmaing
+                * down the SCB at the beginning of the queue.
+                * This could be problematic if either the first,
+                * or the second SCB is removed from the queue
+                * (the first SCB includes a pointer to the "next"
+                * SCB to dma). If we have removed any entries, swap
+                * the first element in the queue with the next HSCB
+                * so the sequencer will notice that NEXT_QUEUED_SCB
+                * has changed during its dma attempt and will retry
+                * the DMA.
+                */
+               scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinstart]);
+
+               if (scb == NULL) {
+                       printf("found = %d, qinstart = %d, qinfifionext = %d\n",
+                               found, qinstart, ahc->qinfifonext);
+                       panic("First/Second Qinfifo fixup\n");
+               }
+               /*
+                * ahc_swap_with_next_hscb forces our next pointer to
+                * point to the reserved SCB for future commands.  Save
+                * and restore our original next pointer to maintain
+                * queue integrity.
+                */
+               next = scb->hscb->next;
+               ahc->scb_data->scbindex[scb->hscb->tag] = NULL;
+               ahc_swap_with_next_hscb(ahc, scb);
+               scb->hscb->next = next;
+               ahc->qinfifo[qinstart] = scb->hscb->tag;
+
+               /* Tell the card about the new head of the qinfifo. */
+               ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
+
+               /* Fixup the tail "next" pointer. */
+               qintail = ahc->qinfifonext - 1;
+               scb = ahc_lookup_scb(ahc, ahc->qinfifo[qintail]);
+               scb->hscb->next = ahc->next_queued_scb->hscb->tag;
+       }
+
+       /*
+        * Search waiting for selection list.
+        */
+       curscbptr = ahc_inb(ahc, SCBPTR);
+       next = ahc_inb(ahc, WAITING_SCBH);  /* Start at head of list. */
+       prev = SCB_LIST_NULL;
+
+       while (next != SCB_LIST_NULL) {
+               uint8_t scb_index;
+
+               ahc_outb(ahc, SCBPTR, next);
+               scb_index = ahc_inb(ahc, SCB_TAG);
+               if (scb_index >= ahc->scb_data->numscbs) {
+                       printf("Waiting List inconsistency. "
+                              "SCB index == %d, yet numscbs == %d.",
+                              scb_index, ahc->scb_data->numscbs);
+                       ahc_dump_card_state(ahc);
+                       panic("for safety");
+               }
+               scb = ahc_lookup_scb(ahc, scb_index);
+               if (scb == NULL) {
+                       printf("scb_index = %d, next = %d\n",
+                               scb_index, next);
+                       panic("Waiting List traversal\n");
+               }
+               if (ahc_match_scb(ahc, scb, target, channel,
+                                 lun, SCB_LIST_NULL, role)) {
+                       /*
+                        * We found an scb that needs to be acted on.
+                        */
+                       found++;
+                       switch (action) {
+                       case SEARCH_COMPLETE:
+                       {
+                               cam_status ostat;
+                               cam_status cstat;
+
+                               ostat = ahc_get_transaction_status(scb);
+                               if (ostat == CAM_REQ_INPROG)
+                                       ahc_set_transaction_status(scb,
+                                                                  status);
+                               cstat = ahc_get_transaction_status(scb);
+                               if (cstat != CAM_REQ_CMP)
+                                       ahc_freeze_scb(scb);
+                               if ((scb->flags & SCB_ACTIVE) == 0)
+                                       printf("Inactive SCB in Waiting List\n");
+                               ahc_done(ahc, scb);
+                               /* FALLTHROUGH */
+                       }
+                       case SEARCH_REMOVE:
+                               next = ahc_rem_wscb(ahc, next, prev);
+                               break;
+                       case SEARCH_COUNT:
+                               prev = next;
+                               next = ahc_inb(ahc, SCB_NEXT);
+                               break;
+                       }
+               } else {
+                       
+                       prev = next;
+                       next = ahc_inb(ahc, SCB_NEXT);
+               }
+       }
+       ahc_outb(ahc, SCBPTR, curscbptr);
+
+       found += ahc_search_untagged_queues(ahc, /*ahc_io_ctx_t*/NULL, target,
+                                           channel, lun, status, action);
+
+       if (action == SEARCH_COMPLETE)
+               ahc_release_untagged_queues(ahc);
+       return (found);
+}
+
+int
+ahc_search_untagged_queues(struct ahc_softc *ahc, ahc_io_ctx_t ctx,
+                          int target, char channel, int lun, uint32_t status,
+                          ahc_search_action action)
+{
+       struct  scb *scb;
+       int     maxtarget;
+       int     found;
+       int     i;
+
+       if (action == SEARCH_COMPLETE) {
+               /*
+                * Don't attempt to run any queued untagged transactions
+                * until we are done with the abort process.
+                */
+               ahc_freeze_untagged_queues(ahc);
+       }
+
+       found = 0;
+       i = 0;
+       if ((ahc->flags & AHC_SCB_BTT) == 0) {
+
+               maxtarget = 16;
+               if (target != CAM_TARGET_WILDCARD) {
+
+                       i = target;
+                       if (channel == 'B')
+                               i += 8;
+                       maxtarget = i + 1;
+               }
+       } else {
+               maxtarget = 0;
+       }
+
+       for (; i < maxtarget; i++) {
+               struct scb_tailq *untagged_q;
+               struct scb *next_scb;
+
+               untagged_q = &(ahc->untagged_queues[i]);
+               next_scb = TAILQ_FIRST(untagged_q);
+               while (next_scb != NULL) {
+
+                       scb = next_scb;
+                       next_scb = TAILQ_NEXT(scb, links.tqe);
+
+                       /*
+                        * The head of the list may be the currently
+                        * active untagged command for a device.
+                        * We're only searching for commands that
+                        * have not been started.  A transaction
+                        * marked active but still in the qinfifo
+                        * is removed by the qinfifo scanning code
+                        * above.
+                        */
+                       if ((scb->flags & SCB_ACTIVE) != 0)
+                               continue;
+
+                       if (ahc_match_scb(ahc, scb, target, channel, lun,
+                                         SCB_LIST_NULL, ROLE_INITIATOR) == 0
+                        || (ctx != NULL && ctx != scb->io_ctx))
+                               continue;
+
+                       /*
+                        * We found an scb that needs to be acted on.
+                        */
+                       found++;
+                       switch (action) {
+                       case SEARCH_COMPLETE:
+                       {
+                               cam_status ostat;
+                               cam_status cstat;
+
+                               ostat = ahc_get_transaction_status(scb);
+                               if (ostat == CAM_REQ_INPROG)
+                                       ahc_set_transaction_status(scb, status);
+                               cstat = ahc_get_transaction_status(scb);
+                               if (cstat != CAM_REQ_CMP)
+                                       ahc_freeze_scb(scb);
+                               if ((scb->flags & SCB_ACTIVE) == 0)
+                                       printf("Inactive SCB in untaggedQ\n");
+                               ahc_done(ahc, scb);
+                               break;
+                       }
+                       case SEARCH_REMOVE:
+                               TAILQ_REMOVE(untagged_q, scb, links.tqe);
+                               break;
+                       case SEARCH_COUNT:
+                               break;
+                       }
+               }
+       }
+
+       if (action == SEARCH_COMPLETE)
+               ahc_release_untagged_queues(ahc);
+       return (found);
+}
+
+int
+ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel,
+                    int lun, u_int tag, int stop_on_first, int remove,
+                    int save_state)
+{
+       struct  scb *scbp;
+       u_int   next;
+       u_int   prev;
+       u_int   count;
+       u_int   active_scb;
+
+       count = 0;
+       next = ahc_inb(ahc, DISCONNECTED_SCBH);
+       prev = SCB_LIST_NULL;
+
+       if (save_state) {
+               /* restore this when we're done */
+               active_scb = ahc_inb(ahc, SCBPTR);
+       } else
+               /* Silence compiler */
+               active_scb = SCB_LIST_NULL;
+
+       while (next != SCB_LIST_NULL) {
+               u_int scb_index;
+
+               ahc_outb(ahc, SCBPTR, next);
+               scb_index = ahc_inb(ahc, SCB_TAG);
+               if (scb_index >= ahc->scb_data->numscbs) {
+                       printf("Disconnected List inconsistency. "
+                              "SCB index == %d, yet numscbs == %d.",
+                              scb_index, ahc->scb_data->numscbs);
+                       ahc_dump_card_state(ahc);
+                       panic("for safety");
+               }
+
+               if (next == prev) {
+                       panic("Disconnected List Loop. "
+                             "cur SCBPTR == %x, prev SCBPTR == %x.",
+                             next, prev);
+               }
+               scbp = ahc_lookup_scb(ahc, scb_index);
+               if (ahc_match_scb(ahc, scbp, target, channel, lun,
+                                 tag, ROLE_INITIATOR)) {
+                       count++;
+                       if (remove) {
+                               next =
+                                   ahc_rem_scb_from_disc_list(ahc, prev, next);
+                       } else {
+                               prev = next;
+                               next = ahc_inb(ahc, SCB_NEXT);
+                       }
+                       if (stop_on_first)
+                               break;
+               } else {
+                       prev = next;
+                       next = ahc_inb(ahc, SCB_NEXT);
+               }
+       }
+       if (save_state)
+               ahc_outb(ahc, SCBPTR, active_scb);
+       return (count);
+}
+
+/*
+ * Remove an SCB from the on chip list of disconnected transactions.
+ * This is empty/unused if we are not performing SCB paging.
+ */
+static u_int
+ahc_rem_scb_from_disc_list(struct ahc_softc *ahc, u_int prev, u_int scbptr)
+{
+       u_int next;
+
+       ahc_outb(ahc, SCBPTR, scbptr);
+       next = ahc_inb(ahc, SCB_NEXT);
+
+       ahc_outb(ahc, SCB_CONTROL, 0);
+
+       ahc_add_curscb_to_free_list(ahc);
+
+       if (prev != SCB_LIST_NULL) {
+               ahc_outb(ahc, SCBPTR, prev);
+               ahc_outb(ahc, SCB_NEXT, next);
+       } else
+               ahc_outb(ahc, DISCONNECTED_SCBH, next);
+
+       return (next);
+}
+
+/*
+ * Add the SCB as selected by SCBPTR onto the on chip list of
+ * free hardware SCBs.  This list is empty/unused if we are not
+ * performing SCB paging.
+ */
+static void
+ahc_add_curscb_to_free_list(struct ahc_softc *ahc)
+{
+       /*
+        * Invalidate the tag so that our abort
+        * routines don't think it's active.
+        */
+       ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+
+       if ((ahc->flags & AHC_PAGESCBS) != 0) {
+               ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH));
+               ahc_outb(ahc, FREE_SCBH, ahc_inb(ahc, SCBPTR));
+       }
+}
+
+/*
+ * Manipulate the waiting for selection list and return the
+ * scb that follows the one that we remove.
+ */
+static u_int
+ahc_rem_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
+{       
+       u_int curscb, next;
+
+       /*
+        * Select the SCB we want to abort and
+        * pull the next pointer out of it.
+        */
+       curscb = ahc_inb(ahc, SCBPTR);
+       ahc_outb(ahc, SCBPTR, scbpos);
+       next = ahc_inb(ahc, SCB_NEXT);
+
+       /* Clear the necessary fields */
+       ahc_outb(ahc, SCB_CONTROL, 0);
+
+       ahc_add_curscb_to_free_list(ahc);
+
+       /* update the waiting list */
+       if (prev == SCB_LIST_NULL) {
+               /* First in the list */
+               ahc_outb(ahc, WAITING_SCBH, next); 
+
+               /*
+                * Ensure we aren't attempting to perform
+                * selection for this entry.
+                */
+               ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+       } else {
+               /*
+                * Select the scb that pointed to us 
+                * and update its next pointer.
+                */
+               ahc_outb(ahc, SCBPTR, prev);
+               ahc_outb(ahc, SCB_NEXT, next);
+       }
+
+       /*
+        * Point us back at the original scb position.
+        */
+       ahc_outb(ahc, SCBPTR, curscb);
+       return next;
+}
+
+/******************************** Error Handling ******************************/
+/*
+ * Abort all SCBs that match the given description (target/channel/lun/tag),
+ * setting their status to the passed in status if the status has not already
+ * been modified from CAM_REQ_INPROG.  This routine assumes that the sequencer
+ * is paused before it is called.
+ */
+int
+ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel,
+              int lun, u_int tag, role_t role, uint32_t status)
+{
+       struct  scb *scbp;
+       struct  scb *scbp_next;
+       u_int   active_scb;
+       int     i, j;
+       int     maxtarget;
+       int     minlun;
+       int     maxlun;
+
+       int     found;
+
+       /*
+        * Don't attempt to run any queued untagged transactions
+        * until we are done with the abort process.
+        */
+       ahc_freeze_untagged_queues(ahc);
+
+       /* restore this when we're done */
+       active_scb = ahc_inb(ahc, SCBPTR);
+
+       found = ahc_search_qinfifo(ahc, target, channel, lun, SCB_LIST_NULL,
+                                  role, CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+       /*
+        * Clean out the busy target table for any untagged commands.
+        */
+       i = 0;
+       maxtarget = 16;
+       if (target != CAM_TARGET_WILDCARD) {
+               i = target;
+               if (channel == 'B')
+                       i += 8;
+               maxtarget = i + 1;
+       }
+
+       if (lun == CAM_LUN_WILDCARD) {
+
+               /*
+                * Unless we are using an SCB based
+                * busy targets table, there is only
+                * one table entry for all luns of
+                * a target.
+                */
+               minlun = 0;
+               maxlun = 1;
+               if ((ahc->flags & AHC_SCB_BTT) != 0)
+                       maxlun = AHC_NUM_LUNS;
+       } else {
+               minlun = lun;
+               maxlun = lun + 1;
+       }
+
+       if (role != ROLE_TARGET) {
+               for (;i < maxtarget; i++) {
+                       for (j = minlun;j < maxlun; j++) {
+                               u_int scbid;
+                               u_int tcl;
+
+                               tcl = BUILD_TCL(i << 4, j);
+                               scbid = ahc_index_busy_tcl(ahc, tcl);
+                               scbp = ahc_lookup_scb(ahc, scbid);
+                               if (scbp == NULL
+                                || ahc_match_scb(ahc, scbp, target, channel,
+                                                 lun, tag, role) == 0)
+                                       continue;
+                               ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, j));
+                       }
+               }
+
+               /*
+                * Go through the disconnected list and remove any entries we
+                * have queued for completion, 0'ing their control byte too.
+                * We save the active SCB and restore it ourselves, so there
+                * is no reason for this search to restore it too.
+                */
+               ahc_search_disc_list(ahc, target, channel, lun, tag,
+                                    /*stop_on_first*/FALSE, /*remove*/TRUE,
+                                    /*save_state*/FALSE);
+       }
+
+       /*
+        * Go through the hardware SCB array looking for commands that
+        * were active but not on any list.  In some cases, these remnants
+        * might not still have mappings in the scbindex array (e.g. unexpected
+        * bus free with the same scb queued for an abort).  Don't hold this
+        * against them.
+        */
+       for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+               u_int scbid;
+
+               ahc_outb(ahc, SCBPTR, i);
+               scbid = ahc_inb(ahc, SCB_TAG);
+               scbp = ahc_lookup_scb(ahc, scbid);
+               if ((scbp == NULL && scbid != SCB_LIST_NULL)
+                || (scbp != NULL
+                 && ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)))
+                       ahc_add_curscb_to_free_list(ahc);
+       }
+
+       /*
+        * Go through the pending CCB list and look for
+        * commands for this target that are still active.
+        * These are other tagged commands that were
+        * disconnected when the reset occurred.
+        */
+       scbp_next = LIST_FIRST(&ahc->pending_scbs);
+       while (scbp_next != NULL) {
+               scbp = scbp_next;
+               scbp_next = LIST_NEXT(scbp, pending_links);
+               if (ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)) {
+                       cam_status ostat;
+
+                       ostat = ahc_get_transaction_status(scbp);
+                       if (ostat == CAM_REQ_INPROG)
+                               ahc_set_transaction_status(scbp, status);
+                       if (ahc_get_transaction_status(scbp) != CAM_REQ_CMP)
+                               ahc_freeze_scb(scbp);
+                       if ((scbp->flags & SCB_ACTIVE) == 0)
+                               printf("Inactive SCB on pending list\n");
+                       ahc_done(ahc, scbp);
+                       found++;
+               }
+       }
+       ahc_outb(ahc, SCBPTR, active_scb);
+       ahc_platform_abort_scbs(ahc, target, channel, lun, tag, role, status);
+       ahc_release_untagged_queues(ahc);
+       return found;
+}
+
+static void
+ahc_reset_current_bus(struct ahc_softc *ahc)
+{
+       uint8_t scsiseq;
+
+       ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENSCSIRST);
+       scsiseq = ahc_inb(ahc, SCSISEQ);
+       ahc_outb(ahc, SCSISEQ, scsiseq | SCSIRSTO);
+       ahc_flush_device_writes(ahc);
+       ahc_delay(AHC_BUSRESET_DELAY);
+       /* Turn off the bus reset */
+       ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO);
+
+       ahc_clear_intstat(ahc);
+
+       /* Re-enable reset interrupts */
+       ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST);
+}
+
+int
+ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
+{
+       struct  ahc_devinfo devinfo;
+       u_int   initiator, target, max_scsiid;
+       u_int   sblkctl;
+       u_int   scsiseq;
+       u_int   simode1;
+       int     found;
+       int     restart_needed;
+       char    cur_channel;
+
+       ahc->pending_device = NULL;
+
+       ahc_compile_devinfo(&devinfo,
+                           CAM_TARGET_WILDCARD,
+                           CAM_TARGET_WILDCARD,
+                           CAM_LUN_WILDCARD,
+                           channel, ROLE_UNKNOWN);
+       ahc_pause(ahc);
+
+       /* Make sure the sequencer is in a safe location. */
+       ahc_clear_critical_section(ahc);
+
+       /*
+        * Run our command complete fifos to ensure that we perform
+        * completion processing on any commands that 'completed'
+        * before the reset occurred.
+        */
+       ahc_run_qoutfifo(ahc);
+#if AHC_TARGET_MODE
+       /*
+        * XXX - In Twin mode, the tqinfifo may have commands
+        *       for an unaffected channel in it.  However, if
+        *       we have run out of ATIO resources to drain that
+        *       queue, we may not get them all out here.  Further,
+        *       the blocked transactions for the reset channel
+        *       should just be killed off, irrespecitve of whether
+        *       we are blocked on ATIO resources.  Write a routine
+        *       to compact the tqinfifo appropriately.
+        */
+       if ((ahc->flags & AHC_TARGETROLE) != 0) {
+               ahc_run_tqinfifo(ahc, /*paused*/TRUE);
+       }
+#endif
+
+       /*
+        * Reset the bus if we are initiating this reset
+        */
+       sblkctl = ahc_inb(ahc, SBLKCTL);
+       cur_channel = 'A';
+       if ((ahc->features & AHC_TWIN) != 0
+        && ((sblkctl & SELBUSB) != 0))
+           cur_channel = 'B';
+       scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+       if (cur_channel != channel) {
+               /* Case 1: Command for another bus is active
+                * Stealthily reset the other bus without
+                * upsetting the current bus.
+                */
+               ahc_outb(ahc, SBLKCTL, sblkctl ^ SELBUSB);
+               simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
+#if AHC_TARGET_MODE
+               /*
+                * Bus resets clear ENSELI, so we cannot
+                * defer re-enabling bus reset interrupts
+                * if we are in target mode.
+                */
+               if ((ahc->flags & AHC_TARGETROLE) != 0)
+                       simode1 |= ENSCSIRST;
+#endif
+               ahc_outb(ahc, SIMODE1, simode1);
+               if (initiate_reset)
+                       ahc_reset_current_bus(ahc);
+               ahc_clear_intstat(ahc);
+               ahc_outb(ahc, SCSISEQ, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
+               ahc_outb(ahc, SBLKCTL, sblkctl);
+               restart_needed = FALSE;
+       } else {
+               /* Case 2: A command from this bus is active or we're idle */
+               simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
+#if AHC_TARGET_MODE
+               /*
+                * Bus resets clear ENSELI, so we cannot
+                * defer re-enabling bus reset interrupts
+                * if we are in target mode.
+                */
+               if ((ahc->flags & AHC_TARGETROLE) != 0)
+                       simode1 |= ENSCSIRST;
+#endif
+               ahc_outb(ahc, SIMODE1, simode1);
+               if (initiate_reset)
+                       ahc_reset_current_bus(ahc);
+               ahc_clear_intstat(ahc);
+               ahc_outb(ahc, SCSISEQ, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
+               restart_needed = TRUE;
+       }
+
+       /*
+        * Clean up all the state information for the
+        * pending transactions on this bus.
+        */
+       found = ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, channel,
+                              CAM_LUN_WILDCARD, SCB_LIST_NULL,
+                              ROLE_UNKNOWN, CAM_SCSI_BUS_RESET);
+
+       max_scsiid = (ahc->features & AHC_WIDE) ? 15 : 7;
+
+#ifdef AHC_TARGET_MODE
+       /*
+        * Send an immediate notify ccb to all target more peripheral
+        * drivers affected by this action.
+        */
+       for (target = 0; target <= max_scsiid; target++) {
+               struct ahc_tmode_tstate* tstate;
+               u_int lun;
+
+               tstate = ahc->enabled_targets[target];
+               if (tstate == NULL)
+                       continue;
+               for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+                       struct ahc_tmode_lstate* lstate;
+
+                       lstate = tstate->enabled_luns[lun];
+                       if (lstate == NULL)
+                               continue;
+
+                       ahc_queue_lstate_event(ahc, lstate, CAM_TARGET_WILDCARD,
+                                              EVENT_TYPE_BUS_RESET, /*arg*/0);
+                       ahc_send_lstate_events(ahc, lstate);
+               }
+       }
+#endif
+       /* Notify the XPT that a bus reset occurred */
+       ahc_send_async(ahc, devinfo.channel, CAM_TARGET_WILDCARD,
+                      CAM_LUN_WILDCARD, AC_BUS_RESET, NULL);
+
+       /*
+        * Revert to async/narrow transfers until we renegotiate.
+        */
+       for (target = 0; target <= max_scsiid; target++) {
+
+               if (ahc->enabled_targets[target] == NULL)
+                       continue;
+               for (initiator = 0; initiator <= max_scsiid; initiator++) {
+                       struct ahc_devinfo devinfo;
+
+                       ahc_compile_devinfo(&devinfo, target, initiator,
+                                           CAM_LUN_WILDCARD,
+                                           channel, ROLE_UNKNOWN);
+                       ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+                                     AHC_TRANS_CUR, /*paused*/TRUE);
+                       ahc_set_syncrate(ahc, &devinfo, /*syncrate*/NULL,
+                                        /*period*/0, /*offset*/0,
+                                        /*ppr_options*/0, AHC_TRANS_CUR,
+                                        /*paused*/TRUE);
+               }
+       }
+
+       if (restart_needed)
+               ahc_restart(ahc);
+       else
+               ahc_unpause(ahc);
+       return found;
+}
+
+
+/***************************** Residual Processing ****************************/
+/*
+ * Calculate the residual for a just completed SCB.
+ */
+void
+ahc_calc_residual(struct ahc_softc *ahc, struct scb *scb)
+{
+       struct hardware_scb *hscb;
+       struct status_pkt *spkt;
+       uint32_t sgptr;
+       uint32_t resid_sgptr;
+       uint32_t resid =0;
+
+       /*
+        * 5 cases.
+        * 1) No residual.
+        *    SG_RESID_VALID clear in sgptr.
+        * 2) Transferless command
+        * 3) Never performed any transfers.
+        *    sgptr has SG_FULL_RESID set.
+        * 4) No residual but target did not
+        *    save data pointers after the
+        *    last transfer, so sgptr was
+        *    never updated.
+        * 5) We have a partial residual.
+        *    Use residual_sgptr to determine
+        *    where we are.
+        */
+
+       hscb = scb->hscb;
+       sgptr = ahc_le32toh(hscb->sgptr);
+       if ((sgptr & SG_RESID_VALID) == 0)
+               /* Case 1 */
+               return;
+       sgptr &= ~SG_RESID_VALID;
+
+       if ((sgptr & SG_LIST_NULL) != 0)
+               /* Case 2 */
+               return;
+
+       spkt = &hscb->shared_data.status;
+       resid_sgptr = ahc_le32toh(spkt->residual_sg_ptr);
+       if ((sgptr & SG_FULL_RESID) != 0) {
+               /* Case 3 */
+               resid = ahc_get_transfer_length(scb);
+       } else if ((resid_sgptr & SG_LIST_NULL) != 0) {
+               /* Case 4 */
+               return;
+       } else if ((resid_sgptr & ~SG_PTR_MASK) != 0) {
+               panic("Bogus resid sgptr value 0x%x\n", resid_sgptr);
+       } else {
+               struct ahc_dma_seg *sg;
+
+               /*
+                * Remainder of the SG where the transfer
+                * stopped.  
+                */
+               resid = ahc_le32toh(spkt->residual_datacnt) & AHC_SG_LEN_MASK;
+               sg = ahc_sg_bus_to_virt(scb, resid_sgptr & SG_PTR_MASK);
+
+               /* The residual sg_ptr always points to the next sg */
+               sg--;
+
+               /*
+                * Add up the contents of all residual
+                * SG segments that are after the SG where
+                * the transfer stopped.
+                */
+               while ((ahc_le32toh(sg->len) & AHC_DMA_LAST_SEG) == 0) {
+                       sg++;
+                       resid += ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
+               }
+       }
+       if ((scb->flags & SCB_SENSE) == 0)
+               ahc_set_residual(scb, resid);
+       else
+               ahc_set_sense_residual(scb, resid);
+
+#ifdef AHC_DEBUG
+       if ((ahc_debug & AHC_SHOWMISC) != 0) {
+               ahc_print_path(ahc, scb);
+               printf("Handled Residual of %d bytes\n", resid);
+       }
+#endif
+}
+
+/******************************* Target Mode **********************************/
+#ifdef AHC_TARGET_MODE
+/*
+ * Add a target mode event to this lun's queue
+ */
+static void
+ahc_queue_lstate_event(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate,
+                      u_int initiator_id, u_int event_type, u_int event_arg)
+{
+       struct ahc_tmode_event *event;
+       int pending;
+
+       xpt_freeze_devq(lstate->path, /*count*/1);
+       if (lstate->event_w_idx >= lstate->event_r_idx)
+               pending = lstate->event_w_idx - lstate->event_r_idx;
+       else
+               pending = AHC_TMODE_EVENT_BUFFER_SIZE + 1
+                       - (lstate->event_r_idx - lstate->event_w_idx);
+
+       if (event_type == EVENT_TYPE_BUS_RESET
+        || event_type == MSG_BUS_DEV_RESET) {
+               /*
+                * Any earlier events are irrelevant, so reset our buffer.
+                * This has the effect of allowing us to deal with reset
+                * floods (an external device holding down the reset line)
+                * without losing the event that is really interesting.
+                */
+               lstate->event_r_idx = 0;
+               lstate->event_w_idx = 0;
+               xpt_release_devq(lstate->path, pending, /*runqueue*/FALSE);
+       }
+
+       if (pending == AHC_TMODE_EVENT_BUFFER_SIZE) {
+               xpt_print_path(lstate->path);
+               printf("immediate event %x:%x lost\n",
+                      lstate->event_buffer[lstate->event_r_idx].event_type,
+                      lstate->event_buffer[lstate->event_r_idx].event_arg);
+               lstate->event_r_idx++;
+               if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+                       lstate->event_r_idx = 0;
+               xpt_release_devq(lstate->path, /*count*/1, /*runqueue*/FALSE);
+       }
+
+       event = &lstate->event_buffer[lstate->event_w_idx];
+       event->initiator_id = initiator_id;
+       event->event_type = event_type;
+       event->event_arg = event_arg;
+       lstate->event_w_idx++;
+       if (lstate->event_w_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+               lstate->event_w_idx = 0;
+}
+
+/*
+ * Send any target mode events queued up waiting
+ * for immediate notify resources.
+ */
+void
+ahc_send_lstate_events(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate)
+{
+       struct ccb_hdr *ccbh;
+       struct ccb_immed_notify *inot;
+
+       while (lstate->event_r_idx != lstate->event_w_idx
+           && (ccbh = SLIST_FIRST(&lstate->immed_notifies)) != NULL) {
+               struct ahc_tmode_event *event;
+
+               event = &lstate->event_buffer[lstate->event_r_idx];
+               SLIST_REMOVE_HEAD(&lstate->immed_notifies, sim_links.sle);
+               inot = (struct ccb_immed_notify *)ccbh;
+               switch (event->event_type) {
+               case EVENT_TYPE_BUS_RESET:
+                       ccbh->status = CAM_SCSI_BUS_RESET|CAM_DEV_QFRZN;
+                       break;
+               default:
+                       ccbh->status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN;
+                       inot->message_args[0] = event->event_type;
+                       inot->message_args[1] = event->event_arg;
+                       break;
+               }
+               inot->initiator_id = event->initiator_id;
+               inot->sense_len = 0;
+               xpt_done((union ccb *)inot);
+               lstate->event_r_idx++;
+               if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+                       lstate->event_r_idx = 0;
+       }
+}
+#endif
+
+/******************** Sequencer Program Patching/Download *********************/
+
+#ifdef AHC_DUMP_SEQ
+void
+ahc_dumpseq(struct ahc_softc* ahc)
+{
+       int i;
+       int max_prog;
+
+       if ((ahc->chip & AHC_BUS_MASK) < AHC_PCI)
+               max_prog = 448;
+       else if ((ahc->features & AHC_ULTRA2) != 0)
+               max_prog = 768;
+       else
+               max_prog = 512;
+
+       ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+       ahc_outb(ahc, SEQADDR0, 0);
+       ahc_outb(ahc, SEQADDR1, 0);
+       for (i = 0; i < max_prog; i++) {
+               uint8_t ins_bytes[4];
+
+               ahc_insb(ahc, SEQRAM, ins_bytes, 4);
+               printf("0x%08x\n", ins_bytes[0] << 24
+                                | ins_bytes[1] << 16
+                                | ins_bytes[2] << 8
+                                | ins_bytes[3]);
+       }
+}
+#endif
+
+static void
+ahc_loadseq(struct ahc_softc *ahc)
+{
+       struct  cs cs_table[num_critical_sections];
+       u_int   begin_set[num_critical_sections];
+       u_int   end_set[num_critical_sections];
+       struct  patch *cur_patch;
+       u_int   cs_count;
+       u_int   cur_cs;
+       u_int   i;
+       int     downloaded;
+       u_int   skip_addr;
+       u_int   sg_prefetch_cnt;
+       uint8_t download_consts[7];
+
+       /*
+        * Start out with 0 critical sections
+        * that apply to this firmware load.
+        */
+       cs_count = 0;
+       cur_cs = 0;
+       memset(begin_set, 0, sizeof(begin_set));
+       memset(end_set, 0, sizeof(end_set));
+
+       /* Setup downloadable constant table */
+       download_consts[QOUTFIFO_OFFSET] = 0;
+       if (ahc->targetcmds != NULL)
+               download_consts[QOUTFIFO_OFFSET] += 32;
+       download_consts[QINFIFO_OFFSET] = download_consts[QOUTFIFO_OFFSET] + 1;
+       download_consts[CACHESIZE_MASK] = ahc->pci_cachesize - 1;
+       download_consts[INVERTED_CACHESIZE_MASK] = ~(ahc->pci_cachesize - 1);
+       sg_prefetch_cnt = ahc->pci_cachesize;
+       if (sg_prefetch_cnt < (2 * sizeof(struct ahc_dma_seg)))
+               sg_prefetch_cnt = 2 * sizeof(struct ahc_dma_seg);
+       download_consts[SG_PREFETCH_CNT] = sg_prefetch_cnt;
+       download_consts[SG_PREFETCH_ALIGN_MASK] = ~(sg_prefetch_cnt - 1);
+       download_consts[SG_PREFETCH_ADDR_MASK] = (sg_prefetch_cnt - 1);
+
+       cur_patch = patches;
+       downloaded = 0;
+       skip_addr = 0;
+       ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+       ahc_outb(ahc, SEQADDR0, 0);
+       ahc_outb(ahc, SEQADDR1, 0);
+
+       for (i = 0; i < sizeof(seqprog)/4; i++) {
+               if (ahc_check_patch(ahc, &cur_patch, i, &skip_addr) == 0) {
+                       /*
+                        * Don't download this instruction as it
+                        * is in a patch that was removed.
+                        */
+                       continue;
+               }
+               /*
+                * Move through the CS table until we find a CS
+                * that might apply to this instruction.
+                */
+               for (; cur_cs < num_critical_sections; cur_cs++) {
+                       if (critical_sections[cur_cs].end <= i) {
+                               if (begin_set[cs_count] == TRUE
+                                && end_set[cs_count] == FALSE) {
+                                       cs_table[cs_count].end = downloaded;
+                                       end_set[cs_count] = TRUE;
+                                       cs_count++;
+                               }
+                               continue;
+                       }
+                       if (critical_sections[cur_cs].begin <= i
+                        && begin_set[cs_count] == FALSE) {
+                               cs_table[cs_count].begin = downloaded;
+                               begin_set[cs_count] = TRUE;
+                       }
+                       break;
+               }
+               ahc_download_instr(ahc, i, download_consts);
+               downloaded++;
+       }
+
+       ahc->num_critical_sections = cs_count;
+       if (cs_count != 0) {
+
+               cs_count *= sizeof(struct cs);
+               ahc->critical_sections = malloc(cs_count, M_DEVBUF, M_NOWAIT);
+               if (ahc->critical_sections == NULL)
+                       panic("ahc_loadseq: Could not malloc");
+               memcpy(ahc->critical_sections, cs_table, cs_count);
+       }
+       ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE);
+       ahc_restart(ahc);
+
+       if (bootverbose)
+               printf(" %d instructions downloaded\n", downloaded);
+}
+
+static int
+ahc_check_patch(struct ahc_softc *ahc, struct patch **start_patch,
+               u_int start_instr, u_int *skip_addr)
+{
+       struct  patch *cur_patch;
+       struct  patch *last_patch;
+       u_int   num_patches;
+
+       num_patches = sizeof(patches)/sizeof(struct patch);
+       last_patch = &patches[num_patches];
+       cur_patch = *start_patch;
+
+       while (cur_patch < last_patch && start_instr == cur_patch->begin) {
+
+               if (cur_patch->patch_func(ahc) == 0) {
+
+                       /* Start rejecting code */
+                       *skip_addr = start_instr + cur_patch->skip_instr;
+                       cur_patch += cur_patch->skip_patch;
+               } else {
+                       /* Accepted this patch.  Advance to the next
+                        * one and wait for our intruction pointer to
+                        * hit this point.
+                        */
+                       cur_patch++;
+               }
+       }
+
+       *start_patch = cur_patch;
+       if (start_instr < *skip_addr)
+               /* Still skipping */
+               return (0);
+
+       return (1);
+}
+
+static void
+ahc_download_instr(struct ahc_softc *ahc, u_int instrptr, uint8_t *dconsts)
+{
+       union   ins_formats instr;
+       struct  ins_format1 *fmt1_ins;
+       struct  ins_format3 *fmt3_ins;
+       u_int   opcode;
+
+       /*
+        * The firmware is always compiled into a little endian format.
+        */
+       instr.integer = ahc_le32toh(*(uint32_t*)&seqprog[instrptr * 4]);
+
+       fmt1_ins = &instr.format1;
+       fmt3_ins = NULL;
+
+       /* Pull the opcode */
+       opcode = instr.format1.opcode;
+       switch (opcode) {
+       case AIC_OP_JMP:
+       case AIC_OP_JC:
+       case AIC_OP_JNC:
+       case AIC_OP_CALL:
+       case AIC_OP_JNE:
+       case AIC_OP_JNZ:
+       case AIC_OP_JE:
+       case AIC_OP_JZ:
+       {
+               struct patch *cur_patch;
+               int address_offset;
+               u_int address;
+               u_int skip_addr;
+               u_int i;
+
+               fmt3_ins = &instr.format3;
+               address_offset = 0;
+               address = fmt3_ins->address;
+               cur_patch = patches;
+               skip_addr = 0;
+
+               for (i = 0; i < address;) {
+
+                       ahc_check_patch(ahc, &cur_patch, i, &skip_addr);
+
+                       if (skip_addr > i) {
+                               int end_addr;
+
+                               end_addr = MIN(address, skip_addr);
+                               address_offset += end_addr - i;
+                               i = skip_addr;
+                       } else {
+                               i++;
+                       }
+               }
+               address -= address_offset;
+               fmt3_ins->address = address;
+               /* FALLTHROUGH */
+       }
+       case AIC_OP_OR:
+       case AIC_OP_AND:
+       case AIC_OP_XOR:
+       case AIC_OP_ADD:
+       case AIC_OP_ADC:
+       case AIC_OP_BMOV:
+               if (fmt1_ins->parity != 0) {
+                       fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+               }
+               fmt1_ins->parity = 0;
+               if ((ahc->features & AHC_CMD_CHAN) == 0
+                && opcode == AIC_OP_BMOV) {
+                       /*
+                        * Block move was added at the same time
+                        * as the command channel.  Verify that
+                        * this is only a move of a single element
+                        * and convert the BMOV to a MOV
+                        * (AND with an immediate of FF).
+                        */
+                       if (fmt1_ins->immediate != 1)
+                               panic("%s: BMOV not supported\n",
+                                     ahc_name(ahc));
+                       fmt1_ins->opcode = AIC_OP_AND;
+                       fmt1_ins->immediate = 0xff;
+               }
+               /* FALLTHROUGH */
+       case AIC_OP_ROL:
+               if ((ahc->features & AHC_ULTRA2) != 0) {
+                       int i, count;
+
+                       /* Calculate odd parity for the instruction */
+                       for (i = 0, count = 0; i < 31; i++) {
+                               uint32_t mask;
+
+                               mask = 0x01 << i;
+                               if ((instr.integer & mask) != 0)
+                                       count++;
+                       }
+                       if ((count & 0x01) == 0)
+                               instr.format1.parity = 1;
+               } else {
+                       /* Compress the instruction for older sequencers */
+                       if (fmt3_ins != NULL) {
+                               instr.integer =
+                                       fmt3_ins->immediate
+                                     | (fmt3_ins->source << 8)
+                                     | (fmt3_ins->address << 16)
+                                     | (fmt3_ins->opcode << 25);
+                       } else {
+                               instr.integer =
+                                       fmt1_ins->immediate
+                                     | (fmt1_ins->source << 8)
+                                     | (fmt1_ins->destination << 16)
+                                     | (fmt1_ins->ret << 24)
+                                     | (fmt1_ins->opcode << 25);
+                       }
+               }
+               /* The sequencer is a little endian cpu */
+               instr.integer = ahc_htole32(instr.integer);
+               ahc_outsb(ahc, SEQRAM, instr.bytes, 4);
+               break;
+       default:
+               panic("Unknown opcode encountered in seq program");
+               break;
+       }
+}
+
+void
+ahc_dump_card_state(struct ahc_softc *ahc)
+{
+       struct scb *scb;
+       struct scb_tailq *untagged_q;
+       int target;
+       int maxtarget;
+       int i;
+       uint8_t last_phase;
+       uint8_t qinpos;
+       uint8_t qintail;
+       uint8_t qoutpos;
+       uint8_t scb_index;
+       uint8_t saved_scbptr;
+
+       saved_scbptr = ahc_inb(ahc, SCBPTR);
+
+       last_phase = ahc_inb(ahc, LASTPHASE);
+       printf("%s: Dumping Card State %s, at SEQADDR 0x%x\n",
+              ahc_name(ahc), ahc_lookup_phase_entry(last_phase)->phasemsg,
+              ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+       printf("ACCUM = 0x%x, SINDEX = 0x%x, DINDEX = 0x%x, ARG_2 = 0x%x\n",
+              ahc_inb(ahc, ACCUM), ahc_inb(ahc, SINDEX), ahc_inb(ahc, DINDEX),
+              ahc_inb(ahc, ARG_2));
+       printf("HCNT = 0x%x SCBPTR = 0x%x\n", ahc_inb(ahc, HCNT),
+              ahc_inb(ahc, SCBPTR));
+       printf("SCSISEQ = 0x%x, SBLKCTL = 0x%x\n",
+              ahc_inb(ahc, SCSISEQ), ahc_inb(ahc, SBLKCTL));
+       printf(" DFCNTRL = 0x%x, DFSTATUS = 0x%x\n",
+              ahc_inb(ahc, DFCNTRL), ahc_inb(ahc, DFSTATUS));
+       printf("LASTPHASE = 0x%x, SCSISIGI = 0x%x, SXFRCTL0 = 0x%x\n",
+              last_phase, ahc_inb(ahc, SCSISIGI), ahc_inb(ahc, SXFRCTL0));
+       printf("SSTAT0 = 0x%x, SSTAT1 = 0x%x\n",
+              ahc_inb(ahc, SSTAT0), ahc_inb(ahc, SSTAT1));
+       if ((ahc->features & AHC_DT) != 0)
+               printf("SCSIPHASE = 0x%x\n", ahc_inb(ahc, SCSIPHASE));
+       printf("STACK == 0x%x, 0x%x, 0x%x, 0x%x\n",
+               ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
+               ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
+               ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
+               ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8));
+       printf("SCB count = %d\n", ahc->scb_data->numscbs);
+       printf("Kernel NEXTQSCB = %d\n", ahc->next_queued_scb->hscb->tag);
+       printf("Card NEXTQSCB = %d\n", ahc_inb(ahc, NEXT_QUEUED_SCB));
+       /* QINFIFO */
+       printf("QINFIFO entries: ");
+       if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+               qinpos = ahc_inb(ahc, SNSCB_QOFF);
+               ahc_outb(ahc, SNSCB_QOFF, qinpos);
+       } else
+               qinpos = ahc_inb(ahc, QINPOS);
+       qintail = ahc->qinfifonext;
+       while (qinpos != qintail) {
+               printf("%d ", ahc->qinfifo[qinpos]);
+               qinpos++;
+       }
+       printf("\n");
+
+       printf("Waiting Queue entries: ");
+       scb_index = ahc_inb(ahc, WAITING_SCBH);
+       i = 0;
+       while (scb_index != SCB_LIST_NULL && i++ < 256) {
+               ahc_outb(ahc, SCBPTR, scb_index);
+               printf("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
+               scb_index = ahc_inb(ahc, SCB_NEXT);
+       }
+       printf("\n");
+
+       printf("Disconnected Queue entries: ");
+       scb_index = ahc_inb(ahc, DISCONNECTED_SCBH);
+       i = 0;
+       while (scb_index != SCB_LIST_NULL && i++ < 256) {
+               ahc_outb(ahc, SCBPTR, scb_index);
+               printf("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
+               scb_index = ahc_inb(ahc, SCB_NEXT);
+       }
+       printf("\n");
+               
+       ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
+       printf("QOUTFIFO entries: ");
+       qoutpos = ahc->qoutfifonext;
+       i = 0;
+       while (ahc->qoutfifo[qoutpos] != SCB_LIST_NULL && i++ < 256) {
+               printf("%d ", ahc->qoutfifo[qoutpos]);
+               qoutpos++;
+       }
+       printf("\n");
+
+       printf("Sequencer Free SCB List: ");
+       scb_index = ahc_inb(ahc, FREE_SCBH);
+       i = 0;
+       while (scb_index != SCB_LIST_NULL && i++ < 256) {
+               ahc_outb(ahc, SCBPTR, scb_index);
+               printf("%d ", scb_index);
+               scb_index = ahc_inb(ahc, SCB_NEXT);
+       }
+       printf("\n");
+
+       printf("Sequencer SCB Info: ");
+       for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+               ahc_outb(ahc, SCBPTR, i);
+               printf("%d(c 0x%x, s 0x%x, l %d, t 0x%x) ",
+                      i, ahc_inb(ahc, SCB_CONTROL),
+                      ahc_inb(ahc, SCB_SCSIID),
+                      ahc_inb(ahc, SCB_LUN),
+                      ahc_inb(ahc, SCB_TAG));
+       }
+       printf("\n");
+
+       printf("Pending list: ");
+       i = 0;
+       LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+               if (i++ > 256)
+                       break;
+               if (scb != LIST_FIRST(&ahc->pending_scbs))
+                       printf(", ");
+               printf("%d(c 0x%x, s 0x%x, l %d)", scb->hscb->tag,
+                      scb->hscb->control, scb->hscb->scsiid, scb->hscb->lun);
+               if ((ahc->flags & AHC_PAGESCBS) == 0) {
+                       ahc_outb(ahc, SCBPTR, scb->hscb->tag);
+                       printf("(0x%x, 0x%x)", ahc_inb(ahc, SCB_CONTROL),
+                              ahc_inb(ahc, SCB_TAG));
+               }
+       }
+       printf("\n");
+
+       printf("Kernel Free SCB list: ");
+       i = 0;
+       SLIST_FOREACH(scb, &ahc->scb_data->free_scbs, links.sle) {
+               if (i++ > 256)
+                       break;
+               printf("%d ", scb->hscb->tag);
+       }
+       printf("\n");
+
+       maxtarget = (ahc->features & (AHC_WIDE|AHC_TWIN)) ? 15 : 7;
+       for (target = 0; target <= maxtarget; target++) {
+               untagged_q = &ahc->untagged_queues[target];
+               if (TAILQ_FIRST(untagged_q) == NULL)
+                       continue;
+               printf("Untagged Q(%d): ", target);
+               i = 0;
+               TAILQ_FOREACH(scb, untagged_q, links.tqe) {
+                       if (i++ > 256)
+                               break;
+                       printf("%d ", scb->hscb->tag);
+               }
+               printf("\n");
+       }
+
+       ahc_platform_dump_card_state(ahc);
+       ahc_outb(ahc, SCBPTR, saved_scbptr);
+}
+
+/************************* Target Mode ****************************************/
+#ifdef AHC_TARGET_MODE
+cam_status
+ahc_find_tmode_devs(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb,
+                   struct ahc_tmode_tstate **tstate,
+                   struct ahc_tmode_lstate **lstate,
+                   int notfound_failure)
+{
+
+       if ((ahc->features & AHC_TARGETMODE) == 0)
+               return (CAM_REQ_INVALID);
+
+       /*
+        * Handle the 'black hole' device that sucks up
+        * requests to unattached luns on enabled targets.
+        */
+       if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD
+        && ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
+               *tstate = NULL;
+               *lstate = ahc->black_hole;
+       } else {
+               u_int max_id;
+
+               max_id = (ahc->features & AHC_WIDE) ? 15 : 7;
+               if (ccb->ccb_h.target_id > max_id)
+                       return (CAM_TID_INVALID);
+
+               if (ccb->ccb_h.target_lun >= AHC_NUM_LUNS)
+                       return (CAM_LUN_INVALID);
+
+               *tstate = ahc->enabled_targets[ccb->ccb_h.target_id];
+               *lstate = NULL;
+               if (*tstate != NULL)
+                       *lstate =
+                           (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
+       }
+
+       if (notfound_failure != 0 && *lstate == NULL)
+               return (CAM_PATH_INVALID);
+
+       return (CAM_REQ_CMP);
+}
+
+void
+ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
+{
+       struct     ahc_tmode_tstate *tstate;
+       struct     ahc_tmode_lstate *lstate;
+       struct     ccb_en_lun *cel;
+       cam_status status;
+       u_int      target;
+       u_int      lun;
+       u_int      target_mask;
+       u_long     s;
+       char       channel;
+
+       status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, &lstate,
+                                    /*notfound_failure*/FALSE);
+
+       if (status != CAM_REQ_CMP) {
+               ccb->ccb_h.status = status;
+               return;
+       }
+
+       if ((ahc->features & AHC_MULTIROLE) != 0) {
+               u_int      our_id;
+
+               if (cam_sim_bus(sim) == 0)
+                       our_id = ahc->our_id;
+               else
+                       our_id = ahc->our_id_b;
+
+               if (ccb->ccb_h.target_id != our_id) {
+                       if ((ahc->features & AHC_MULTI_TID) != 0
+                        && (ahc->flags & AHC_INITIATORROLE) != 0) {
+                               /*
+                                * Only allow additional targets if
+                                * the initiator role is disabled.
+                                * The hardware cannot handle a re-select-in
+                                * on the initiator id during a re-select-out
+                                * on a different target id.
+                                */
+                               status = CAM_TID_INVALID;
+                       } else if ((ahc->flags & AHC_INITIATORROLE) != 0
+                               || ahc->enabled_luns > 0) {
+                               /*
+                                * Only allow our target id to change
+                                * if the initiator role is not configured
+                                * and there are no enabled luns which
+                                * are attached to the currently registered
+                                * scsi id.
+                                */
+                               status = CAM_TID_INVALID;
+                       }
+               }
+       }
+
+       if (status != CAM_REQ_CMP) {
+               ccb->ccb_h.status = status;
+               return;
+       }
+
+       /*
+        * We now have an id that is valid.
+        * If we aren't in target mode, switch modes.
+        */
+       if ((ahc->flags & AHC_TARGETROLE) == 0
+        && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+               u_long  s;
+
+               printf("Configuring Target Mode\n");
+               ahc_lock(ahc, &s);
+               if (LIST_FIRST(&ahc->pending_scbs) != NULL) {
+                       ccb->ccb_h.status = CAM_BUSY;
+                       ahc_unlock(ahc, &s);
+                       return;
+               }
+               ahc->flags |= AHC_TARGETROLE;
+               if ((ahc->features & AHC_MULTIROLE) == 0)
+                       ahc->flags &= ~AHC_INITIATORROLE;
+               ahc_pause(ahc);
+               ahc_loadseq(ahc);
+               ahc_unlock(ahc, &s);
+       }
+       cel = &ccb->cel;
+       target = ccb->ccb_h.target_id;
+       lun = ccb->ccb_h.target_lun;
+       channel = SIM_CHANNEL(ahc, sim);
+       target_mask = 0x01 << target;
+       if (channel == 'B')
+               target_mask <<= 8;
+
+       if (cel->enable != 0) {
+               u_int scsiseq;
+
+               /* Are we already enabled?? */
+               if (lstate != NULL) {
+                       xpt_print_path(ccb->ccb_h.path);
+                       printf("Lun already enabled\n");
+                       ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+                       return;
+               }
+
+               if (cel->grp6_len != 0
+                || cel->grp7_len != 0) {
+                       /*
+                        * Don't (yet?) support vendor
+                        * specific commands.
+                        */
+                       ccb->ccb_h.status = CAM_REQ_INVALID;
+                       printf("Non-zero Group Codes\n");
+                       return;
+               }
+
+               /*
+                * Seems to be okay.
+                * Setup our data structures.
+                */
+               if (target != CAM_TARGET_WILDCARD && tstate == NULL) {
+                       tstate = ahc_alloc_tstate(ahc, target, channel);
+                       if (tstate == NULL) {
+                               xpt_print_path(ccb->ccb_h.path);
+                               printf("Couldn't allocate tstate\n");
+                               ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+                               return;
+                       }
+               }
+               lstate = malloc(sizeof(*lstate), M_DEVBUF, M_NOWAIT);
+               if (lstate == NULL) {
+                       xpt_print_path(ccb->ccb_h.path);
+                       printf("Couldn't allocate lstate\n");
+                       ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+                       return;
+               }
+               memset(lstate, 0, sizeof(*lstate));
+               status = xpt_create_path(&lstate->path, /*periph*/NULL,
+                                        xpt_path_path_id(ccb->ccb_h.path),
+                                        xpt_path_target_id(ccb->ccb_h.path),
+                                        xpt_path_lun_id(ccb->ccb_h.path));
+               if (status != CAM_REQ_CMP) {
+                       free(lstate, M_DEVBUF);
+                       xpt_print_path(ccb->ccb_h.path);
+                       printf("Couldn't allocate path\n");
+                       ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+                       return;
+               }
+               SLIST_INIT(&lstate->accept_tios);
+               SLIST_INIT(&lstate->immed_notifies);
+               ahc_lock(ahc, &s);
+               ahc_pause(ahc);
+               if (target != CAM_TARGET_WILDCARD) {
+                       tstate->enabled_luns[lun] = lstate;
+                       ahc->enabled_luns++;
+
+                       if ((ahc->features & AHC_MULTI_TID) != 0) {
+                               u_int targid_mask;
+
+                               targid_mask = ahc_inb(ahc, TARGID)
+                                           | (ahc_inb(ahc, TARGID + 1) << 8);
+
+                               targid_mask |= target_mask;
+                               ahc_outb(ahc, TARGID, targid_mask);
+                               ahc_outb(ahc, TARGID+1, (targid_mask >> 8));
+                               
+                               ahc_update_scsiid(ahc, targid_mask);
+                       } else {
+                               u_int our_id;
+                               char  channel;
+
+                               channel = SIM_CHANNEL(ahc, sim);
+                               our_id = SIM_SCSI_ID(ahc, sim);
+
+                               /*
+                                * This can only happen if selections
+                                * are not enabled
+                                */
+                               if (target != our_id) {
+                                       u_int sblkctl;
+                                       char  cur_channel;
+                                       int   swap;
+
+                                       sblkctl = ahc_inb(ahc, SBLKCTL);
+                                       cur_channel = (sblkctl & SELBUSB)
+                                                   ? 'B' : 'A';
+                                       if ((ahc->features & AHC_TWIN) == 0)
+                                               cur_channel = 'A';
+                                       swap = cur_channel != channel;
+                                       if (channel == 'A')
+                                               ahc->our_id = target;
+                                       else
+                                               ahc->our_id_b = target;
+
+                                       if (swap)
+                                               ahc_outb(ahc, SBLKCTL,
+                                                        sblkctl ^ SELBUSB);
+
+                                       ahc_outb(ahc, SCSIID, target);
+
+                                       if (swap)
+                                               ahc_outb(ahc, SBLKCTL, sblkctl);
+                               }
+                       }
+               } else
+                       ahc->black_hole = lstate;
+               /* Allow select-in operations */
+               if (ahc->black_hole != NULL && ahc->enabled_luns > 0) {
+                       scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+                       scsiseq |= ENSELI;
+                       ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+                       scsiseq = ahc_inb(ahc, SCSISEQ);
+                       scsiseq |= ENSELI;
+                       ahc_outb(ahc, SCSISEQ, scsiseq);
+               }
+               ahc_unpause(ahc);
+               ahc_unlock(ahc, &s);
+               ccb->ccb_h.status = CAM_REQ_CMP;
+               xpt_print_path(ccb->ccb_h.path);
+               printf("Lun now enabled for target mode\n");
+       } else {
+               struct scb *scb;
+               int i, empty;
+
+               if (lstate == NULL) {
+                       ccb->ccb_h.status = CAM_LUN_INVALID;
+                       return;
+               }
+
+               ahc_lock(ahc, &s);
+               
+               ccb->ccb_h.status = CAM_REQ_CMP;
+               LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+                       struct ccb_hdr *ccbh;
+
+                       ccbh = &scb->io_ctx->ccb_h;
+                       if (ccbh->func_code == XPT_CONT_TARGET_IO
+                        && !xpt_path_comp(ccbh->path, ccb->ccb_h.path)){
+                               printf("CTIO pending\n");
+                               ccb->ccb_h.status = CAM_REQ_INVALID;
+                               ahc_unlock(ahc, &s);
+                               return;
+                       }
+               }
+
+               if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
+                       printf("ATIOs pending\n");
+                       ccb->ccb_h.status = CAM_REQ_INVALID;
+               }
+
+               if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
+                       printf("INOTs pending\n");
+                       ccb->ccb_h.status = CAM_REQ_INVALID;
+               }
+
+               if (ccb->ccb_h.status != CAM_REQ_CMP) {
+                       ahc_unlock(ahc, &s);
+                       return;
+               }
+
+               xpt_print_path(ccb->ccb_h.path);
+               printf("Target mode disabled\n");
+               xpt_free_path(lstate->path);
+               free(lstate, M_DEVBUF);
+
+               ahc_pause(ahc);
+               /* Can we clean up the target too? */
+               if (target != CAM_TARGET_WILDCARD) {
+                       tstate->enabled_luns[lun] = NULL;
+                       ahc->enabled_luns--;
+                       for (empty = 1, i = 0; i < 8; i++)
+                               if (tstate->enabled_luns[i] != NULL) {
+                                       empty = 0;
+                                       break;
+                               }
+
+                       if (empty) {
+                               ahc_free_tstate(ahc, target, channel,
+                                               /*force*/FALSE);
+                               if (ahc->features & AHC_MULTI_TID) {
+                                       u_int targid_mask;
+
+                                       targid_mask = ahc_inb(ahc, TARGID)
+                                                   | (ahc_inb(ahc, TARGID + 1)
+                                                      << 8);
+
+                                       targid_mask &= ~target_mask;
+                                       ahc_outb(ahc, TARGID, targid_mask);
+                                       ahc_outb(ahc, TARGID+1,
+                                                (targid_mask >> 8));
+                                       ahc_update_scsiid(ahc, targid_mask);
+                               }
+                       }
+               } else {
+
+                       ahc->black_hole = NULL;
+
+                       /*
+                        * We can't allow selections without
+                        * our black hole device.
+                        */
+                       empty = TRUE;
+               }
+               if (ahc->enabled_luns == 0) {
+                       /* Disallow select-in */
+                       u_int scsiseq;
+
+                       scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+                       scsiseq &= ~ENSELI;
+                       ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+                       scsiseq = ahc_inb(ahc, SCSISEQ);
+                       scsiseq &= ~ENSELI;
+                       ahc_outb(ahc, SCSISEQ, scsiseq);
+
+                       if ((ahc->features & AHC_MULTIROLE) == 0) {
+                               printf("Configuring Initiator Mode\n");
+                               ahc->flags &= ~AHC_TARGETROLE;
+                               ahc->flags |= AHC_INITIATORROLE;
+                               ahc_pause(ahc);
+                               ahc_loadseq(ahc);
+                       }
+               }
+               ahc_unpause(ahc);
+               ahc_unlock(ahc, &s);
+       }
+}
+
+static void
+ahc_update_scsiid(struct ahc_softc *ahc, u_int targid_mask)
+{
+       u_int scsiid_mask;
+       u_int scsiid;
+
+       if ((ahc->features & AHC_MULTI_TID) == 0)
+               panic("ahc_update_scsiid called on non-multitid unit\n");
+
+       /*
+        * Since we will rely on the the TARGID mask
+        * for selection enables, ensure that OID
+        * in SCSIID is not set to some other ID
+        * that we don't want to allow selections on.
+        */
+       if ((ahc->features & AHC_ULTRA2) != 0)
+               scsiid = ahc_inb(ahc, SCSIID_ULTRA2);
+       else
+               scsiid = ahc_inb(ahc, SCSIID);
+       scsiid_mask = 0x1 << (scsiid & OID);
+       if ((targid_mask & scsiid_mask) == 0) {
+               u_int our_id;
+
+               /* ffs counts from 1 */
+               our_id = ffs(targid_mask);
+               if (our_id == 0)
+                       our_id = ahc->our_id;
+               else
+                       our_id--;
+               scsiid &= TID;
+               scsiid |= our_id;
+       }
+       if ((ahc->features & AHC_ULTRA2) != 0)
+               ahc_outb(ahc, SCSIID_ULTRA2, scsiid);
+       else
+               ahc_outb(ahc, SCSIID, scsiid);
+}
+
+void
+ahc_run_tqinfifo(struct ahc_softc *ahc, int paused)
+{
+       struct target_cmd *cmd;
+
+       /*
+        * If the card supports auto-access pause,
+        * we can access the card directly regardless
+        * of whether it is paused or not.
+        */
+       if ((ahc->features & AHC_AUTOPAUSE) != 0)
+               paused = TRUE;
+
+       ahc_sync_tqinfifo(ahc, BUS_DMASYNC_POSTREAD);
+       while ((cmd = &ahc->targetcmds[ahc->tqinfifonext])->cmd_valid != 0) {
+
+               /*
+                * Only advance through the queue if we
+                * have the resources to process the command.
+                */
+               if (ahc_handle_target_cmd(ahc, cmd) != 0)
+                       break;
+
+               cmd->cmd_valid = 0;
+               ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+                               ahc->shared_data_dmamap,
+                               ahc_targetcmd_offset(ahc, ahc->tqinfifonext),
+                               sizeof(struct target_cmd),
+                               BUS_DMASYNC_PREREAD);
+               ahc->tqinfifonext++;
+
+               /*
+                * Lazily update our position in the target mode incoming
+                * command queue as seen by the sequencer.
+                */
+               if ((ahc->tqinfifonext & (HOST_TQINPOS - 1)) == 1) {
+                       if ((ahc->features & AHC_HS_MAILBOX) != 0) {
+                               u_int hs_mailbox;
+
+                               hs_mailbox = ahc_inb(ahc, HS_MAILBOX);
+                               hs_mailbox &= ~HOST_TQINPOS;
+                               hs_mailbox |= ahc->tqinfifonext & HOST_TQINPOS;
+                               ahc_outb(ahc, HS_MAILBOX, hs_mailbox);
+                       } else {
+                               if (!paused)
+                                       ahc_pause(ahc); 
+                               ahc_outb(ahc, KERNEL_TQINPOS,
+                                        ahc->tqinfifonext & HOST_TQINPOS);
+                               if (!paused)
+                                       ahc_unpause(ahc);
+                       }
+               }
+       }
+}
+
+static int
+ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd)
+{
+       struct    ahc_tmode_tstate *tstate;
+       struct    ahc_tmode_lstate *lstate;
+       struct    ccb_accept_tio *atio;
+       uint8_t *byte;
+       int       initiator;
+       int       target;
+       int       lun;
+
+       initiator = SCSIID_TARGET(ahc, cmd->scsiid);
+       target = SCSIID_OUR_ID(cmd->scsiid);
+       lun    = (cmd->identify & MSG_IDENTIFY_LUNMASK);
+
+       byte = cmd->bytes;
+       tstate = ahc->enabled_targets[target];
+       lstate = NULL;
+       if (tstate != NULL)
+               lstate = tstate->enabled_luns[lun];
+
+       /*
+        * Commands for disabled luns go to the black hole driver.
+        */
+       if (lstate == NULL)
+               lstate = ahc->black_hole;
+
+       atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
+       if (atio == NULL) {
+               ahc->flags |= AHC_TQINFIFO_BLOCKED;
+               /*
+                * Wait for more ATIOs from the peripheral driver for this lun.
+                */
+               if (bootverbose)
+                       printf("%s: ATIOs exhausted\n", ahc_name(ahc));
+               return (1);
+       } else
+               ahc->flags &= ~AHC_TQINFIFO_BLOCKED;
+#if 0
+       printf("Incoming command from %d for %d:%d%s\n",
+              initiator, target, lun,
+              lstate == ahc->black_hole ? "(Black Holed)" : "");
+#endif
+       SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
+
+       if (lstate == ahc->black_hole) {
+               /* Fill in the wildcards */
+               atio->ccb_h.target_id = target;
+               atio->ccb_h.target_lun = lun;
+       }
+
+       /*
+        * Package it up and send it off to
+        * whomever has this lun enabled.
+        */
+       atio->sense_len = 0;
+       atio->init_id = initiator;
+       if (byte[0] != 0xFF) {
+               /* Tag was included */
+               atio->tag_action = *byte++;
+               atio->tag_id = *byte++;
+               atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
+       } else {
+               atio->ccb_h.flags = 0;
+       }
+       byte++;
+
+       /* Okay.  Now determine the cdb size based on the command code */
+       switch (*byte >> CMD_GROUP_CODE_SHIFT) {
+       case 0:
+               atio->cdb_len = 6;
+               break;
+       case 1:
+       case 2:
+               atio->cdb_len = 10;
+               break;
+       case 4:
+               atio->cdb_len = 16;
+               break;
+       case 5:
+               atio->cdb_len = 12;
+               break;
+       case 3:
+       default:
+               /* Only copy the opcode. */
+               atio->cdb_len = 1;
+               printf("Reserved or VU command code type encountered\n");
+               break;
+       }
+       
+       memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
+
+       atio->ccb_h.status |= CAM_CDB_RECVD;
+
+       if ((cmd->identify & MSG_IDENTIFY_DISCFLAG) == 0) {
+               /*
+                * We weren't allowed to disconnect.
+                * We're hanging on the bus until a
+                * continue target I/O comes in response
+                * to this accept tio.
+                */
+#if 0
+               printf("Received Immediate Command %d:%d:%d - %p\n",
+                      initiator, target, lun, ahc->pending_device);
+#endif
+               ahc->pending_device = lstate;
+               ahc_freeze_ccb((union ccb *)atio);
+               atio->ccb_h.flags |= CAM_DIS_DISCONNECT;
+       }
+       xpt_done((union ccb*)atio);
+       return (0);
+}
+
+#endif
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_host.h b/xen/drivers/scsi/aic7xxx/aic7xxx_host.h
new file mode 100644 (file)
index 0000000..23710c6
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Adaptec AIC7xxx device driver host template for Linux.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_host.h#9 $
+ */
+
+#ifndef _AIC7XXX_HOST_H_
+#define _AIC7XXX_HOST_H_
+
+#ifdef CONFIG_PROC_FS
+int             ahc_linux_proc_info(char *, char **, off_t, int, int, int);
+#endif
+int             ahc_linux_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+int             ahc_linux_detect(Scsi_Host_Template *);
+int             ahc_linux_release(struct Scsi_Host *);
+const char     *ahc_linux_info(struct Scsi_Host *);
+int             ahc_linux_biosparam(Disk *, kdev_t, int[]);
+int             ahc_linux_bus_reset(Scsi_Cmnd *);
+int             ahc_linux_dev_reset(Scsi_Cmnd *);
+int             ahc_linux_abort(Scsi_Cmnd *);
+
+#if defined(__i386__)
+#  define AIC7XXX_BIOSPARAM ahc_linux_biosparam
+#else
+#  define AIC7XXX_BIOSPARAM NULL
+#endif
+
+/*
+ * Scsi_Host_Template (see hosts.h) for AIC-7xxx - some fields
+ * to do with card config are filled in after the card is detected.
+ */
+
+//     proc_dir: NULL,                                         
+  //   proc_info: ahc_linux_proc_info,                         
+
+
+#define AIC7XXX        {                                               \
+       next: NULL,                                             \
+       module: NULL,                                           \
+       name: NULL,                                             \
+       detect: ahc_linux_detect,                               \
+       release: ahc_linux_release,                             \
+       info: ahc_linux_info,                                   \
+       command: NULL,                                          \
+       queuecommand: ahc_linux_queue,                          \
+       eh_strategy_handler: NULL,                              \
+       eh_abort_handler: ahc_linux_abort,                      \
+       eh_device_reset_handler: ahc_linux_dev_reset,           \
+       eh_bus_reset_handler: ahc_linux_bus_reset,              \
+       eh_host_reset_handler: NULL,                            \
+       abort: NULL,                                            \
+       reset: NULL,                                            \
+       slave_attach: NULL,                                     \
+       bios_param: AIC7XXX_BIOSPARAM,                          \
+       can_queue: AHC_MAX_QUEUE,/* max simultaneous cmds     */\
+       this_id: -1,             /* scsi id of host adapter   */\
+       sg_tablesize: AHC_NSEG,  /* max scatter-gather cmds   */\
+       cmd_per_lun: 2,          /* cmds per lun              */\
+       present: 0,              /* number of 7xxx's present  */\
+       unchecked_isa_dma: 0,    /* no memory DMA restrictions*/\
+       use_clustering: ENABLE_CLUSTERING,                      \
+       use_new_eh_code: 1                                      \
+}
+
+/*     highmem_io: 1                                           \
+ */
+
+#endif /* _AIC7XXX_HOST_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_inline.h b/xen/drivers/scsi/aic7xxx/aic7xxx_inline.h
new file mode 100644 (file)
index 0000000..2d774f2
--- /dev/null
@@ -0,0 +1,563 @@
+/*
+ * Inline routines shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_inline.h#35 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_inline.h,v 1.2.2.11 2002/04/29 19:36:31 gibbs Exp $
+ */
+
+#ifndef _AIC7XXX_INLINE_H_
+#define _AIC7XXX_INLINE_H_
+
+/************************* Sequencer Execution Control ************************/
+static __inline void ahc_pause_bug_fix(struct ahc_softc *ahc);
+static __inline int  ahc_is_paused(struct ahc_softc *ahc);
+static __inline void ahc_pause(struct ahc_softc *ahc);
+static __inline void ahc_unpause(struct ahc_softc *ahc);
+
+/*
+ * Work around any chip bugs related to halting sequencer execution.
+ * On Ultra2 controllers, we must clear the CIOBUS stretch signal by
+ * reading a register that will set this signal and deassert it.
+ * Without this workaround, if the chip is paused, by an interrupt or
+ * manual pause while accessing scb ram, accesses to certain registers
+ * will hang the system (infinite pci retries).
+ */
+static __inline void
+ahc_pause_bug_fix(struct ahc_softc *ahc)
+{
+       if ((ahc->features & AHC_ULTRA2) != 0)
+               (void)ahc_inb(ahc, CCSCBCTL);
+}
+
+/*
+ * Determine whether the sequencer has halted code execution.
+ * Returns non-zero status if the sequencer is stopped.
+ */
+static __inline int
+ahc_is_paused(struct ahc_softc *ahc)
+{
+       return ((ahc_inb(ahc, HCNTRL) & PAUSE) != 0);
+}
+
+/*
+ * Request that the sequencer stop and wait, indefinitely, for it
+ * to stop.  The sequencer will only acknowledge that it is paused
+ * once it has reached an instruction boundary and PAUSEDIS is
+ * cleared in the SEQCTL register.  The sequencer may use PAUSEDIS
+ * for critical sections.
+ */
+static __inline void
+ahc_pause(struct ahc_softc *ahc)
+{
+       ahc_outb(ahc, HCNTRL, ahc->pause);
+
+       /*
+        * Since the sequencer can disable pausing in a critical section, we
+        * must loop until it actually stops.
+        */
+       while (ahc_is_paused(ahc) == 0)
+               ;
+
+       ahc_pause_bug_fix(ahc);
+}
+
+/*
+ * Allow the sequencer to continue program execution.
+ * We check here to ensure that no additional interrupt
+ * sources that would cause the sequencer to halt have been
+ * asserted.  If, for example, a SCSI bus reset is detected
+ * while we are fielding a different, pausing, interrupt type,
+ * we don't want to release the sequencer before going back
+ * into our interrupt handler and dealing with this new
+ * condition.
+ */
+static __inline void
+ahc_unpause(struct ahc_softc *ahc)
+{
+       if ((ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)
+               ahc_outb(ahc, HCNTRL, ahc->unpause);
+}
+
+/*********************** Untagged Transaction Routines ************************/
+static __inline void   ahc_freeze_untagged_queues(struct ahc_softc *ahc);
+static __inline void   ahc_release_untagged_queues(struct ahc_softc *ahc);
+
+/*
+ * Block our completion routine from starting the next untagged
+ * transaction for this target or target lun.
+ */
+static __inline void
+ahc_freeze_untagged_queues(struct ahc_softc *ahc)
+{
+       if ((ahc->flags & AHC_SCB_BTT) == 0)
+               ahc->untagged_queue_lock++;
+}
+
+/*
+ * Allow the next untagged transaction for this target or target lun
+ * to be executed.  We use a counting semaphore to allow the lock
+ * to be acquired recursively.  Once the count drops to zero, the
+ * transaction queues will be run.
+ */
+static __inline void
+ahc_release_untagged_queues(struct ahc_softc *ahc)
+{
+       if ((ahc->flags & AHC_SCB_BTT) == 0) {
+               ahc->untagged_queue_lock--;
+               if (ahc->untagged_queue_lock == 0)
+                       ahc_run_untagged_queues(ahc);
+       }
+}
+
+/************************** Memory mapping routines ***************************/
+static __inline struct ahc_dma_seg *
+                       ahc_sg_bus_to_virt(struct scb *scb,
+                                          uint32_t sg_busaddr);
+static __inline uint32_t
+                       ahc_sg_virt_to_bus(struct scb *scb,
+                                          struct ahc_dma_seg *sg);
+static __inline uint32_t
+                       ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index);
+static __inline void   ahc_sync_scb(struct ahc_softc *ahc,
+                                    struct scb *scb, int op);
+static __inline void   ahc_sync_sglist(struct ahc_softc *ahc,
+                                       struct scb *scb, int op);
+static __inline uint32_t
+                       ahc_targetcmd_offset(struct ahc_softc *ahc,
+                                            u_int index);
+
+static __inline struct ahc_dma_seg *
+ahc_sg_bus_to_virt(struct scb *scb, uint32_t sg_busaddr)
+{
+       int sg_index;
+
+       sg_index = (sg_busaddr - scb->sg_list_phys)/sizeof(struct ahc_dma_seg);
+       /* sg_list_phys points to entry 1, not 0 */
+       sg_index++;
+
+       return (&scb->sg_list[sg_index]);
+}
+
+static __inline uint32_t
+ahc_sg_virt_to_bus(struct scb *scb, struct ahc_dma_seg *sg)
+{
+       int sg_index;
+
+       /* sg_list_phys points to entry 1, not 0 */
+       sg_index = sg - &scb->sg_list[1];
+
+       return (scb->sg_list_phys + (sg_index * sizeof(*scb->sg_list)));
+}
+
+static __inline uint32_t
+ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index)
+{
+       return (ahc->scb_data->hscb_busaddr
+               + (sizeof(struct hardware_scb) * index));
+}
+
+static __inline void
+ahc_sync_scb(struct ahc_softc *ahc, struct scb *scb, int op)
+{
+       ahc_dmamap_sync(ahc, ahc->scb_data->hscb_dmat,
+                       ahc->scb_data->hscb_dmamap,
+                       /*offset*/(scb->hscb - ahc->hscbs) * sizeof(*scb->hscb),
+                       /*len*/sizeof(*scb->hscb), op);
+}
+
+static __inline void
+ahc_sync_sglist(struct ahc_softc *ahc, struct scb *scb, int op)
+{
+       if (scb->sg_count == 0)
+               return;
+
+       ahc_dmamap_sync(ahc, ahc->scb_data->sg_dmat, scb->sg_map->sg_dmamap,
+                       /*offset*/(scb->sg_list - scb->sg_map->sg_vaddr)
+                               * sizeof(struct ahc_dma_seg),
+                       /*len*/sizeof(struct ahc_dma_seg) * scb->sg_count, op);
+}
+
+static __inline uint32_t
+ahc_targetcmd_offset(struct ahc_softc *ahc, u_int index)
+{
+       return (((uint8_t *)&ahc->targetcmds[index]) - ahc->qoutfifo);
+}
+
+/******************************** Debugging ***********************************/
+static __inline char *ahc_name(struct ahc_softc *ahc);
+
+static __inline char *
+ahc_name(struct ahc_softc *ahc)
+{
+       return (ahc->name);
+}
+
+/*********************** Miscelaneous Support Functions ***********************/
+
+static __inline void   ahc_update_residual(struct ahc_softc *ahc,
+                                           struct scb *scb);
+static __inline struct ahc_initiator_tinfo *
+                       ahc_fetch_transinfo(struct ahc_softc *ahc,
+                                           char channel, u_int our_id,
+                                           u_int remote_id,
+                                           struct ahc_tmode_tstate **tstate);
+static __inline struct scb*
+                       ahc_get_scb(struct ahc_softc *ahc);
+static __inline void   ahc_free_scb(struct ahc_softc *ahc, struct scb *scb);
+static __inline void   ahc_swap_with_next_hscb(struct ahc_softc *ahc,
+                                               struct scb *scb);
+static __inline void   ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb);
+static __inline struct scsi_sense_data *
+                       ahc_get_sense_buf(struct ahc_softc *ahc,
+                                         struct scb *scb);
+static __inline uint32_t
+                       ahc_get_sense_bufaddr(struct ahc_softc *ahc,
+                                             struct scb *scb);
+
+/*
+ * Determine whether the sequencer reported a residual
+ * for this SCB/transaction.
+ */
+static __inline void
+ahc_update_residual(struct ahc_softc *ahc, struct scb *scb)
+{
+       uint32_t sgptr;
+
+       sgptr = ahc_le32toh(scb->hscb->sgptr);
+       if ((sgptr & SG_RESID_VALID) != 0)
+               ahc_calc_residual(ahc, scb);
+}
+
+/*
+ * Return pointers to the transfer negotiation information
+ * for the specified our_id/remote_id pair.
+ */
+static __inline struct ahc_initiator_tinfo *
+ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id,
+                   u_int remote_id, struct ahc_tmode_tstate **tstate)
+{
+       /*
+        * Transfer data structures are stored from the perspective
+        * of the target role.  Since the parameters for a connection
+        * in the initiator role to a given target are the same as
+        * when the roles are reversed, we pretend we are the target.
+        */
+       if (channel == 'B')
+               our_id += 8;
+       *tstate = ahc->enabled_targets[our_id];
+       return (&(*tstate)->transinfo[remote_id]);
+}
+
+/*
+ * Get a free scb. If there are none, see if we can allocate a new SCB.
+ */
+static __inline struct scb *
+ahc_get_scb(struct ahc_softc *ahc)
+{
+       struct scb *scb;
+
+       if ((scb = SLIST_FIRST(&ahc->scb_data->free_scbs)) == NULL) {
+               ahc_alloc_scbs(ahc);
+               scb = SLIST_FIRST(&ahc->scb_data->free_scbs);
+               if (scb == NULL)
+                       return (NULL);
+       }
+       SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links.sle);
+       return (scb);
+}
+
+/*
+ * Return an SCB resource to the free list.
+ */
+static __inline void
+ahc_free_scb(struct ahc_softc *ahc, struct scb *scb)
+{       
+       struct hardware_scb *hscb;
+
+       hscb = scb->hscb;
+       /* Clean up for the next user */
+       ahc->scb_data->scbindex[hscb->tag] = NULL;
+       scb->flags = SCB_FREE;
+       hscb->control = 0;
+
+       SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs, scb, links.sle);
+
+       /* Notify the OSM that a resource is now available. */
+       ahc_platform_scb_free(ahc, scb);
+}
+
+static __inline struct scb *
+ahc_lookup_scb(struct ahc_softc *ahc, u_int tag)
+{
+       struct scb* scb;
+
+       scb = ahc->scb_data->scbindex[tag];
+       if (scb != NULL)
+               ahc_sync_scb(ahc, scb,
+                            BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+       return (scb);
+}
+
+static __inline void
+ahc_swap_with_next_hscb(struct ahc_softc *ahc, struct scb *scb)
+{
+       struct hardware_scb *q_hscb;
+       u_int  saved_tag;
+
+       /*
+        * Our queuing method is a bit tricky.  The card
+        * knows in advance which HSCB to download, and we
+        * can't disappoint it.  To achieve this, the next
+        * SCB to download is saved off in ahc->next_queued_scb.
+        * When we are called to queue "an arbitrary scb",
+        * we copy the contents of the incoming HSCB to the one
+        * the sequencer knows about, swap HSCB pointers and
+        * finally assign the SCB to the tag indexed location
+        * in the scb_array.  This makes sure that we can still
+        * locate the correct SCB by SCB_TAG.
+        */
+       q_hscb = ahc->next_queued_scb->hscb;
+       saved_tag = q_hscb->tag;
+       memcpy(q_hscb, scb->hscb, sizeof(*scb->hscb));
+       if ((scb->flags & SCB_CDB32_PTR) != 0) {
+               q_hscb->shared_data.cdb_ptr =
+                   ahc_htole32(ahc_hscb_busaddr(ahc, q_hscb->tag)
+                             + offsetof(struct hardware_scb, cdb32));
+       }
+       q_hscb->tag = saved_tag;
+       q_hscb->next = scb->hscb->tag;
+
+       /* Now swap HSCB pointers. */
+       ahc->next_queued_scb->hscb = scb->hscb;
+       scb->hscb = q_hscb;
+
+       /* Now define the mapping from tag to SCB in the scbindex */
+       ahc->scb_data->scbindex[scb->hscb->tag] = scb;
+}
+
+/*
+ * Tell the sequencer about a new transaction to execute.
+ */
+static __inline void
+ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+       ahc_swap_with_next_hscb(ahc, scb);
+
+       if (scb->hscb->tag == SCB_LIST_NULL
+        || scb->hscb->next == SCB_LIST_NULL)
+               panic("Attempt to queue invalid SCB tag %x:%x\n",
+                     scb->hscb->tag, scb->hscb->next);
+
+       /*
+        * Keep a history of SCBs we've downloaded in the qinfifo.
+        */
+       ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+
+       /*
+        * Make sure our data is consistant from the
+        * perspective of the adapter.
+        */
+       ahc_sync_scb(ahc, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+       /* Tell the adapter about the newly queued SCB */
+       if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+               ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+       } else {
+               if ((ahc->features & AHC_AUTOPAUSE) == 0)
+                       ahc_pause(ahc);
+               ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+               if ((ahc->features & AHC_AUTOPAUSE) == 0)
+                       ahc_unpause(ahc);
+       }
+}
+
+static __inline struct scsi_sense_data *
+ahc_get_sense_buf(struct ahc_softc *ahc, struct scb *scb)
+{
+       int offset;
+
+       offset = scb - ahc->scb_data->scbarray;
+       return (&ahc->scb_data->sense[offset]);
+}
+
+static __inline uint32_t
+ahc_get_sense_bufaddr(struct ahc_softc *ahc, struct scb *scb)
+{
+       int offset;
+
+       offset = scb - ahc->scb_data->scbarray;
+       return (ahc->scb_data->sense_busaddr
+             + (offset * sizeof(struct scsi_sense_data)));
+}
+
+/************************** Interrupt Processing ******************************/
+static __inline void   ahc_sync_qoutfifo(struct ahc_softc *ahc, int op);
+static __inline void   ahc_sync_tqinfifo(struct ahc_softc *ahc, int op);
+static __inline u_int  ahc_check_cmdcmpltqueues(struct ahc_softc *ahc);
+static __inline void   ahc_intr(struct ahc_softc *ahc);
+
+static __inline void
+ahc_sync_qoutfifo(struct ahc_softc *ahc, int op)
+{
+       ahc_dmamap_sync(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
+                       /*offset*/0, /*len*/256, op);
+}
+
+static __inline void
+ahc_sync_tqinfifo(struct ahc_softc *ahc, int op)
+{
+#ifdef AHC_TARGET_MODE
+       if ((ahc->flags & AHC_TARGETROLE) != 0) {
+               ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+                               ahc->shared_data_dmamap,
+                               ahc_targetcmd_offset(ahc, 0),
+                               sizeof(struct target_cmd) * AHC_TMODE_CMDS,
+                               op);
+       }
+#endif
+}
+
+/*
+ * See if the firmware has posted any completed commands
+ * into our in-core command complete fifos.
+ */
+#define AHC_RUN_QOUTFIFO 0x1
+#define AHC_RUN_TQINFIFO 0x2
+static __inline u_int
+ahc_check_cmdcmpltqueues(struct ahc_softc *ahc)
+{
+       u_int retval;
+
+       retval = 0;
+       ahc_dmamap_sync(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
+                       /*offset*/ahc->qoutfifonext, /*len*/1,
+                       BUS_DMASYNC_POSTREAD);
+       if (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL)
+               retval |= AHC_RUN_QOUTFIFO;
+#ifdef AHC_TARGET_MODE
+       if ((ahc->flags & AHC_TARGETROLE) != 0
+        && (ahc->flags & AHC_TQINFIFO_BLOCKED) == 0) {
+               ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+                               ahc->shared_data_dmamap,
+                               ahc_targetcmd_offset(ahc, ahc->tqinfifofnext),
+                               /*len*/sizeof(struct target_cmd),
+                               BUS_DMASYNC_POSTREAD);
+               if (ahc->targetcmds[ahc->tqinfifonext].cmd_valid != 0)
+                       retval |= AHC_RUN_TQINFIFO;
+       }
+#endif
+       return (retval);
+}
+
+/*
+ * Catch an interrupt from the adapter
+ */
+static __inline void
+ahc_intr(struct ahc_softc *ahc)
+{
+       u_int   intstat;
+
+       /*
+        * Instead of directly reading the interrupt status register,
+        * infer the cause of the interrupt by checking our in-core
+        * completion queues.  This avoids a costly PCI bus read in
+        * most cases.
+        */
+       if ((ahc->flags & (AHC_ALL_INTERRUPTS|AHC_EDGE_INTERRUPT)) == 0
+        && (ahc_check_cmdcmpltqueues(ahc) != 0))
+               intstat = CMDCMPLT;
+       else {
+               intstat = ahc_inb(ahc, INTSTAT);
+       }
+
+       if (intstat & CMDCMPLT) {
+               ahc_outb(ahc, CLRINT, CLRCMDINT);
+
+               /*
+                * Ensure that the chip sees that we've cleared
+                * this interrupt before we walk the output fifo.
+                * Otherwise, we may, due to posted bus writes,
+                * clear the interrupt after we finish the scan,
+                * and after the sequencer has added new entries
+                * and asserted the interrupt again.
+                */
+               ahc_flush_device_writes(ahc);
+               ahc_run_qoutfifo(ahc);
+#ifdef AHC_TARGET_MODE
+               if ((ahc->flags & AHC_TARGETROLE) != 0)
+                       ahc_run_tqinfifo(ahc, /*paused*/FALSE);
+#endif
+       }
+
+       if (intstat == 0xFF && (ahc->features & AHC_REMOVABLE) != 0)
+               /* Hot eject */
+               return;
+
+       if ((intstat & INT_PEND) == 0) {
+#if AHC_PCI_CONFIG > 0
+               if (ahc->unsolicited_ints > 500) {
+                       ahc->unsolicited_ints = 0;
+                       if ((ahc->chip & AHC_PCI) != 0
+                        && (ahc_inb(ahc, ERROR) & PCIERRSTAT) != 0)
+                               ahc->bus_intr(ahc);
+               }
+#endif
+               ahc->unsolicited_ints++;
+               return;
+       }
+       ahc->unsolicited_ints = 0;
+
+       if (intstat & BRKADRINT) {
+               ahc_handle_brkadrint(ahc);
+               /* Fatal error, no more interrupts to handle. */
+               return;
+       }
+
+       if ((intstat & (SEQINT|SCSIINT)) != 0)
+               ahc_pause_bug_fix(ahc);
+
+       if ((intstat & SEQINT) != 0)
+               ahc_handle_seqint(ahc, intstat);
+
+       if ((intstat & SCSIINT) != 0)
+               ahc_handle_scsiint(ahc, intstat);
+}
+
+#endif  /* _AIC7XXX_INLINE_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_osm.c b/xen/drivers/scsi/aic7xxx/aic7xxx_osm.c
new file mode 100644 (file)
index 0000000..e868f32
--- /dev/null
@@ -0,0 +1,3071 @@
+/*
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.c#103 $
+ *
+ * Copyright (c) 1994 John Aycock
+ *   The University of Calgary Department of Computer Science.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Sources include the Adaptec 1740 driver (aha1740.c), the Ultrastor 24F
+ * driver (ultrastor.c), various Linux kernel source, the Adaptec EISA
+ * config file (!adp7771.cfg), the Adaptec AHA-2740A Series User's Guide,
+ * the Linux Kernel Hacker's Guide, Writing a SCSI Device Driver for Linux,
+ * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA overlay file
+ * (adp7770.ovl), the Adaptec AHA-2740 Series Technical Reference Manual,
+ * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
+ * ANSI SCSI-2 specification (draft 10c), ...
+ *
+ * --------------------------------------------------------------------------
+ *
+ *  Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org):
+ *
+ *  Substantially modified to include support for wide and twin bus
+ *  adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
+ *  SCB paging, and other rework of the code.
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-2000 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ *---------------------------------------------------------------------------
+ *
+ *  Thanks also go to (in alphabetical order) the following:
+ *
+ *    Rory Bolt     - Sequencer bug fixes
+ *    Jay Estabrook - Initial DEC Alpha support
+ *    Doug Ledford  - Much needed abort/reset bug fixes
+ *    Kai Makisara  - DMAing of SCBs
+ *
+ *  A Boot time option was also added for not resetting the scsi bus.
+ *
+ *    Form:  aic7xxx=extended
+ *           aic7xxx=no_reset
+ *           aic7xxx=verbose
+ *
+ *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
+ *
+ *  Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp
+ */
+
+/*
+ * Further driver modifications made by Doug Ledford <dledford@redhat.com>
+ *
+ * Copyright (c) 1997-1999 Doug Ledford
+ *
+ * These changes are released under the same licensing terms as the FreeBSD
+ * driver written by Justin Gibbs.  Please see his Copyright notice above
+ * for the exact terms and conditions covering my changes as well as the
+ * warranty statement.
+ *
+ * Modifications made to the aic7xxx.c,v 4.1 driver from Dan Eischen include
+ * but are not limited to:
+ *
+ *  1: Import of the latest FreeBSD sequencer code for this driver
+ *  2: Modification of kernel code to accomodate different sequencer semantics
+ *  3: Extensive changes throughout kernel portion of driver to improve
+ *     abort/reset processing and error hanndling
+ *  4: Other work contributed by various people on the Internet
+ *  5: Changes to printk information and verbosity selection code
+ *  6: General reliability related changes, especially in IRQ management
+ *  7: Modifications to the default probe/attach order for supported cards
+ *  8: SMP friendliness has been improved
+ *
+ */
+
+/*
+ * This is the only file where module.h should
+ * embed module global version info.
+ */
+//#define AHC_MODVERSION_FILE
+
+#include <xeno/lib.h>
+#include <xeno/string.h>
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+#include <xeno/init.h>         /* __setup */
+#endif
+
+#include "../sd.h"             /* For geometry detection */
+
+#include <xeno/mm.h>           /* For fetching system memory size */
+#include <xeno/blk.h>          /* For block_size() */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+/*
+ * Lock protecting manipulation of the ahc softc list.
+ */
+spinlock_t ahc_list_spinlock;
+#endif
+
+/*
+ * To generate the correct addresses for the controller to issue
+ * on the bus.  Originally added for DEC Alpha support.
+ */
+#define VIRT_TO_BUS(a) (uint32_t)virt_to_bus((void *)(a))
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+struct proc_dir_entry proc_scsi_aic7xxx = {
+       PROC_SCSI_AIC7XXX, 7, "aic7xxx",
+       S_IFDIR | S_IRUGO | S_IXUGO, 2,
+       0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+#endif
+
+/*
+ * Set this to the delay in seconds after SCSI bus reset.
+ * Note, we honor this only for the initial bus reset.
+ * The scsi error recovery code performs its own bus settle
+ * delay handling for error recovery actions.
+ */
+#ifdef CONFIG_AIC7XXX_RESET_DELAY_MS
+#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY_MS
+#else
+#define AIC7XXX_RESET_DELAY 500
+#endif
+
+/*
+ * Control collection of SCSI transfer statistics for the /proc filesystem.
+ *
+ * NOTE: Do NOT enable this when running on kernels version 1.2.x and below.
+ * NOTE: This does affect performance since it has to maintain statistics.
+ */
+#ifdef CONFIG_AIC7XXX_PROC_STATS
+#define AIC7XXX_PROC_STATS
+#endif
+
+/*
+ * To change the default number of tagged transactions allowed per-device,
+ * add a line to the lilo.conf file like:
+ * append="aic7xxx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}"
+ * which will result in the first four devices on the first two
+ * controllers being set to a tagged queue depth of 32.
+ *
+ * The tag_commands is an array of 16 to allow for wide and twin adapters.
+ * Twin adapters will use indexes 0-7 for channel 0, and indexes 8-15
+ * for channel 1.
+ */
+typedef struct {
+       uint8_t tag_commands[16];       /* Allow for wide/twin adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Modify this as you see fit for your system.
+ *
+ * 0                   tagged queuing disabled
+ * 1 <= n <= 253       n == max tags ever dispatched.
+ *
+ * The driver will throttle the number of commands dispatched to a
+ * device if it returns queue full.  For devices with a fixed maximum
+ * queue depth, the driver will eventually determine this depth and
+ * lock it in (a console message is printed to indicate that a lock
+ * has occurred).  On some devices, queue full is returned for a temporary
+ * resource shortage.  These devices will return queue full at varying
+ * depths.  The driver will throttle back when the queue fulls occur and
+ * attempt to slowly increase the depth over time as the device recovers
+ * from the resource shortage.
+ *
+ * In this example, the first line will disable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (0, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to attempt to use up to 64 tags for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3.  It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+
+/*
+ * NOTE: The below structure is for reference only, the actual structure
+ *       to modify in order to change things is just below this comment block.
+adapter_tag_info_t aic7xxx_tag_info[] =
+{
+       {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+       {{4, 64, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4}},
+       {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+       {{0, 16, 4, 0, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+*/
+
+#ifdef CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#define AIC7XXX_CMDS_PER_DEVICE CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#else
+#define AIC7XXX_CMDS_PER_DEVICE AHC_MAX_QUEUE
+#endif
+
+#define AIC7XXX_CONFIGED_TAG_COMMANDS {                                        \
+       AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,               \
+       AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,               \
+       AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,               \
+       AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,               \
+       AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,               \
+       AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,               \
+       AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,               \
+       AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE                \
+}
+
+/*
+ * By default, use the number of commands specified by
+ * the users kernel configuration.
+ */
+static adapter_tag_info_t aic7xxx_tag_info[] =
+{
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS},
+       {AIC7XXX_CONFIGED_TAG_COMMANDS}
+};
+
+/*
+ * There should be a specific return value for this in scsi.h, but
+ * it seems that most drivers ignore it.
+ */
+#define DID_UNDERFLOW   DID_ERROR
+
+void
+ahc_print_path(struct ahc_softc *ahc, struct scb *scb)
+{
+       printf("(scsi%d:%c:%d:%d): ",
+              ahc->platform_data->host->host_no,
+              scb != NULL ? SCB_GET_CHANNEL(ahc, scb) : 'X',
+              scb != NULL ? SCB_GET_TARGET(ahc, scb) : -1,
+              scb != NULL ? SCB_GET_LUN(scb) : -1);
+}
+
+/*
+ * XXX - these options apply unilaterally to _all_ 274x/284x/294x
+ *       cards in the system.  This should be fixed.  Exceptions to this
+ *       rule are noted in the comments.
+ */
+
+/*
+ * Skip the scsi bus reset.  Non 0 make us skip the reset at startup.  This
+ * has no effect on any later resets that might occur due to things like
+ * SCSI bus timeouts.
+ */
+static uint32_t aic7xxx_no_reset;
+
+/*
+ * Certain PCI motherboards will scan PCI devices from highest to lowest,
+ * others scan from lowest to highest, and they tend to do all kinds of
+ * strange things when they come into contact with PCI bridge chips.  The
+ * net result of all this is that the PCI card that is actually used to boot
+ * the machine is very hard to detect.  Most motherboards go from lowest
+ * PCI slot number to highest, and the first SCSI controller found is the
+ * one you boot from.  The only exceptions to this are when a controller
+ * has its BIOS disabled.  So, we by default sort all of our SCSI controllers
+ * from lowest PCI slot number to highest PCI slot number.  We also force
+ * all controllers with their BIOS disabled to the end of the list.  This
+ * works on *almost* all computers.  Where it doesn't work, we have this
+ * option.  Setting this option to non-0 will reverse the order of the sort
+ * to highest first, then lowest, but will still leave cards with their BIOS
+ * disabled at the very end.  That should fix everyone up unless there are
+ * really strange cirumstances.
+ */
+static int aic7xxx_reverse_scan = 0;
+
+/*
+ * Should we force EXTENDED translation on a controller.
+ *     0 == Use whatever is in the SEEPROM or default to off
+ *     1 == Use whatever is in the SEEPROM or default to on
+ */
+static uint32_t aic7xxx_extended = 0;
+
+/*
+ * PCI bus parity checking of the Adaptec controllers.  This is somewhat
+ * dubious at best.  To my knowledge, this option has never actually
+ * solved a PCI parity problem, but on certain machines with broken PCI
+ * chipset configurations, it can generate tons of false error messages.
+ * It's included in the driver for completeness.
+ *   0 = Shut off PCI parity check
+ *  -1 = Normal polarity pci parity checking
+ *   1 = reverse polarity pci parity checking
+ *
+ * NOTE: you can't actually pass -1 on the lilo prompt.  So, to set this
+ * variable to -1 you would actually want to simply pass the variable
+ * name without a number.  That will invert the 0 which will result in
+ * -1.
+ */
+static int aic7xxx_pci_parity = 0;
+
+/*
+ * Certain newer motherboards have put new PCI based devices into the
+ * IO spaces that used to typically be occupied by VLB or EISA cards.
+ * This overlap can cause these newer motherboards to lock up when scanned
+ * for older EISA and VLB devices.  Setting this option to non-0 will
+ * cause the driver to skip scanning for any VLB or EISA controllers and
+ * only support the PCI controllers.  NOTE: this means that if the kernel
+ * os compiled with PCI support disabled, then setting this to non-0
+ * would result in never finding any devices :)
+ */
+#ifndef CONFIG_AIC7XXX_PROBE_EISA_VL
+#define CONFIG_AIC7XXX_PROBE_EISA_VL n
+#endif
+#if CONFIG_AIC7XXX_PROBE_EISA_VL == n
+static int aic7xxx_no_probe = 1;
+#else
+static int aic7xxx_no_probe;
+#endif
+
+/*
+ * aic7xxx_detect() has been run, so register all device arrivals
+ * immediately with the system rather than deferring to the sorted
+ * attachment performed by aic7xxx_detect().
+ */
+int aic7xxx_detect_complete;
+
+/*
+ * So that we can set how long each device is given as a selection timeout.
+ * The table of values goes like this:
+ *   0 - 256ms
+ *   1 - 128ms
+ *   2 - 64ms
+ *   3 - 32ms
+ * We default to 256ms because some older devices need a longer time
+ * to respond to initial selection.
+ */
+static int aic7xxx_seltime = 0x00;
+
+/*
+ * Certain devices do not perform any aging on commands.  Should the
+ * device be saturated by commands in one portion of the disk, it is
+ * possible for transactions on far away sectors to never be serviced.
+ * To handle these devices, we can periodically send an ordered tag to
+ * force all outstanding transactions to be serviced prior to a new
+ * transaction.
+ */
+int aic7xxx_periodic_otag;
+
+/*
+ * Module information and settable options.
+ */
+#ifdef MODULE
+static char *aic7xxx = NULL;
+/*
+ * Just in case someone uses commas to separate items on the insmod
+ * command line, we define a dummy buffer here to avoid having insmod
+ * write wild stuff into our code segment
+ */
+static char dummy_buffer[60] = "Please don't trounce on me insmod!!\n";
+/*
+MODULE_AUTHOR("Maintainer: Justin T. Gibbs <gibbs@scsiguy.com>");
+MODULE_DESCRIPTION("Adaptec Aic77XX/78XX SCSI Host Bus Adapter driver");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10)
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+MODULE_PARM(aic7xxx, "s");
+MODULE_PARM_DESC(aic7xxx, "period delimited, options string.
+       verbose                 Enable verbose/diagnostic logging
+       no_probe                Disable EISA/VLB controller probing
+       no_reset                Supress initial bus resets
+       extended                Enable extended geometry on all controllers
+       periodic_otag           Send an ordered tagged transaction periodically
+                               to prevent tag starvation.  This may be
+                               required by some older disk drives/RAID arrays. 
+       reverse_scan            Sort PCI devices highest Bus/Slot to lowest
+       tag_info:<tag_str>      Set per-target tag depth
+       seltime:<int>           Selection Timeout(0/256ms,1/128ms,2/64ms,3/32ms)
+
+       Sample /etc/modules.conf line:
+               Enable verbose logging
+               Disable EISA/VLB probing
+               Set tag depth on Controller 2/Target 2 to 10 tags
+               Shorten the selection timeout to 128ms from its default of 256
+
+       options aic7xxx='\"verbose.no_probe.tag_info:{{}.{}.{..10}}.seltime:1\"'
+");
+*/
+#endif
+
+static void ahc_linux_handle_scsi_status(struct ahc_softc *,
+                                        struct ahc_linux_device *,
+                                        struct scb *);
+static void ahc_linux_filter_command(struct ahc_softc*, Scsi_Cmnd*,
+                                    struct scb*);
+//static void ahc_linux_sem_timeout(u_long arg);
+static void ahc_linux_freeze_sim_queue(struct ahc_softc *ahc);
+static void ahc_linux_release_sim_queue(u_long arg);
+static void ahc_linux_dev_timed_unfreeze(u_long arg);
+static int  ahc_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag);
+static void ahc_linux_initialize_scsi_bus(struct ahc_softc *ahc);
+static void ahc_linux_select_queue_depth(struct Scsi_Host *host,
+                                        Scsi_Device *scsi_devs);
+static u_int ahc_linux_user_tagdepth(struct ahc_softc *ahc,
+                                    struct ahc_devinfo *devinfo);
+static void ahc_linux_device_queue_depth(struct ahc_softc *ahc,
+                                        Scsi_Device *device);
+static struct ahc_linux_target*        ahc_linux_alloc_target(struct ahc_softc*,
+                                                      u_int, u_int);
+static void                    ahc_linux_free_target(struct ahc_softc*,
+                                                     struct ahc_linux_target*);
+static struct ahc_linux_device*        ahc_linux_alloc_device(struct ahc_softc*,
+                                                      struct ahc_linux_target*,
+                                                      u_int);
+static void                    ahc_linux_free_device(struct ahc_softc*,
+                                                     struct ahc_linux_device*);
+static void ahc_linux_run_device_queue(struct ahc_softc*,
+                                      struct ahc_linux_device*);
+static void ahc_linux_setup_tag_info(char *p, char *end);
+static int ahc_linux_next_unit(void);
+static void ahc_runq_tasklet(unsigned long data);
+static int ahc_linux_halt(struct notifier_block *nb, u_long event, void *buf);
+
+static __inline struct ahc_linux_device*
+                    ahc_linux_get_device(struct ahc_softc *ahc, u_int channel,
+                                         u_int target, u_int lun, int alloc);
+static __inline void ahc_linux_queue_cmd_complete(struct ahc_softc *ahc,
+                                                 Scsi_Cmnd *cmd);
+static __inline void ahc_linux_run_complete_queue(struct ahc_softc *ahc,
+                                                 struct ahc_cmd *acmd);
+static __inline void ahc_linux_check_device_queue(struct ahc_softc *ahc,
+                                                 struct ahc_linux_device *dev);
+static __inline struct ahc_linux_device *
+                    ahc_linux_next_device_to_run(struct ahc_softc *ahc);
+static __inline void ahc_linux_run_device_queues(struct ahc_softc *ahc);
+static __inline void ahc_linux_sniff_command(struct ahc_softc*, Scsi_Cmnd*,
+                                            struct scb*);
+static __inline void ahc_linux_unmap_scb(struct ahc_softc*, struct scb*);
+
+static __inline int ahc_linux_map_seg(struct ahc_softc *ahc, struct scb *scb,
+                                     struct ahc_dma_seg *sg,
+                                     bus_addr_t addr, bus_size_t len);
+
+static __inline struct ahc_linux_device*
+ahc_linux_get_device(struct ahc_softc *ahc, u_int channel, u_int target,
+              u_int lun, int alloc)
+{
+       struct ahc_linux_target *targ;
+       struct ahc_linux_device *dev;
+       u_int target_offset;
+
+       target_offset = target;
+       if (channel != 0)
+               target_offset += 8;
+       targ = ahc->platform_data->targets[target_offset];
+       if (targ == NULL) {
+               if (alloc != 0) {
+                       targ = ahc_linux_alloc_target(ahc, channel, target);
+                       if (targ == NULL)
+                               return (NULL);
+               } else
+                       return (NULL);
+       }
+       dev = targ->devices[lun];
+       if (dev == NULL && alloc != 0)
+               dev = ahc_linux_alloc_device(ahc, targ, lun);
+       return (dev);
+}
+
+static __inline void
+ahc_linux_queue_cmd_complete(struct ahc_softc *ahc, Scsi_Cmnd *cmd)
+{
+       /*
+        * Typically, the complete queue has very few entries
+        * queued to it before the queue is emptied by
+        * ahc_linux_run_complete_queue, so sorting the entries
+        * by generation number should be inexpensive.
+        * We perform the sort so that commands that complete
+        * with an error are retuned in the order origionally
+        * queued to the controller so that any subsequent retries
+        * are performed in order.  The underlying ahc routines do
+        * not guarantee the order that aborted commands will be
+        * returned to us.
+        */
+       struct ahc_completeq *completeq;
+       struct ahc_cmd *list_cmd;
+       struct ahc_cmd *acmd;
+
+       /*
+        * If we want the request requeued, make sure there
+        * are sufficent retries.  In the old scsi error code,
+        * we used to be able to specify a result code that
+        * bypassed the retry count.  Now we must use this
+        * hack.
+        */
+       if (cmd->result == (CAM_REQUEUE_REQ << 16))
+               cmd->retries--;
+       completeq = &ahc->platform_data->completeq;
+       list_cmd = TAILQ_FIRST(completeq);
+       acmd = (struct ahc_cmd *)cmd;
+       while (list_cmd != NULL
+           && acmd_scsi_cmd(list_cmd).serial_number
+            < acmd_scsi_cmd(acmd).serial_number)
+               list_cmd = TAILQ_NEXT(list_cmd, acmd_links.tqe);
+       if (list_cmd != NULL)
+               TAILQ_INSERT_BEFORE(list_cmd, acmd, acmd_links.tqe);
+       else
+               TAILQ_INSERT_TAIL(completeq, acmd, acmd_links.tqe);
+}
+
+static __inline void
+ahc_linux_run_complete_queue(struct ahc_softc *ahc, struct ahc_cmd *acmd)
+{      
+       u_long done_flags;
+
+       ahc_done_lock(ahc, &done_flags);
+       while (acmd != NULL) {
+               Scsi_Cmnd *cmd;
+
+               cmd = &acmd_scsi_cmd(acmd);
+               acmd = TAILQ_NEXT(acmd, acmd_links.tqe);
+               cmd->host_scribble = NULL;
+               cmd->scsi_done(cmd);
+       }
+       ahc_done_unlock(ahc, &done_flags);
+}
+
+static __inline void
+ahc_linux_check_device_queue(struct ahc_softc *ahc,
+                            struct ahc_linux_device *dev)
+{
+       if ((dev->flags & AHC_DEV_FREEZE_TIL_EMPTY) != 0
+        && dev->active == 0) {
+               dev->flags &= ~AHC_DEV_FREEZE_TIL_EMPTY;
+               dev->qfrozen--;
+       }
+
+       if (TAILQ_FIRST(&dev->busyq) == NULL
+        || dev->openings == 0 || dev->qfrozen != 0)
+               return;
+
+       ahc_linux_run_device_queue(ahc, dev);
+}
+
+static __inline struct ahc_linux_device *
+ahc_linux_next_device_to_run(struct ahc_softc *ahc)
+{
+       
+       if ((ahc->flags & AHC_RESOURCE_SHORTAGE) != 0
+        || ahc->platform_data->qfrozen != 0)
+               return (NULL);
+       return (TAILQ_FIRST(&ahc->platform_data->device_runq));
+}
+
+static __inline void
+ahc_linux_run_device_queues(struct ahc_softc *ahc)
+{
+       struct ahc_linux_device *dev;
+
+       while ((dev = ahc_linux_next_device_to_run(ahc)) != NULL) {
+               TAILQ_REMOVE(&ahc->platform_data->device_runq, dev, links);
+               dev->flags &= ~AHC_DEV_ON_RUN_LIST;
+               ahc_linux_check_device_queue(ahc, dev);
+       }
+}
+
+static __inline void
+ahc_linux_sniff_command(struct ahc_softc *ahc, Scsi_Cmnd *cmd, struct scb *scb)
+{
+       /*
+        * Determine whether we care to filter
+        * information out of this command.  If so,
+        * pass it on to ahc_linux_filter_command() for more
+        * heavy weight processing.
+        */
+       if (cmd->cmnd[0] == INQUIRY)
+               ahc_linux_filter_command(ahc, cmd, scb);
+}
+
+static __inline void
+ahc_linux_unmap_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+       Scsi_Cmnd *cmd;
+
+       cmd = scb->io_ctx;
+       ahc_sync_sglist(ahc, scb, BUS_DMASYNC_POSTWRITE);
+       if (cmd->use_sg != 0) {
+               struct scatterlist *sg;
+
+               sg = (struct scatterlist *)cmd->request_buffer;
+               pci_unmap_sg(ahc->dev_softc, sg, cmd->use_sg,
+                            scsi_to_pci_dma_dir(cmd->sc_data_direction));
+       } else if (cmd->request_bufflen != 0) {
+               pci_unmap_single(ahc->dev_softc,
+                                scb->platform_data->buf_busaddr,
+                                cmd->request_bufflen,
+                                scsi_to_pci_dma_dir(cmd->sc_data_direction));
+       }
+}
+
+static __inline int
+ahc_linux_map_seg(struct ahc_softc *ahc, struct scb *scb,
+                 struct ahc_dma_seg *sg, bus_addr_t addr, bus_size_t len)
+{
+       int      consumed;
+
+       if ((scb->sg_count + 1) > AHC_NSEG)
+               panic("Too few segs for dma mapping.  "
+                     "Increase AHC_NSEG\n");
+
+       consumed = 1;
+       sg->addr = ahc_htole32(addr & 0xFFFFFFFF);
+       scb->platform_data->xfer_len += len;
+       if (sizeof(bus_addr_t) > 4
+        && (ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+               /*
+                * Due to DAC restrictions, we can't
+                * cross a 4GB boundary.
+                */
+               if ((addr ^ (addr + len - 1)) & ~0xFFFFFFFF) {
+                       struct   ahc_dma_seg *next_sg;
+                       uint32_t next_len;
+
+                       printf("Crossed Seg\n");
+                       if ((scb->sg_count + 2) > AHC_NSEG)
+                               panic("Too few segs for dma mapping.  "
+                                     "Increase AHC_NSEG\n");
+
+                       consumed++;
+                       next_sg = sg + 1;
+                       next_sg->addr = 0;
+                       next_len = 0x100000000 - (addr & 0xFFFFFFFF);
+                       len -= next_len;
+                       next_len |= ((addr >> 8) + 0x1000000) & 0x7F000000;
+                       next_sg->len = ahc_htole32(next_len);
+               }
+               len |= (addr >> 8) & 0x7F000000;
+       }
+       sg->len = ahc_htole32(len);
+       return (consumed);
+}
+
+/**************************** Tasklet Handler *********************************/
+
+static void
+ahc_runq_tasklet(unsigned long data)
+{
+       struct ahc_softc* ahc;
+       struct ahc_linux_device *dev;
+       u_long flags;
+
+       ahc = (struct ahc_softc *)data;
+       ahc_lock(ahc, &flags);
+       while ((dev = ahc_linux_next_device_to_run(ahc)) != NULL) {
+       
+               TAILQ_REMOVE(&ahc->platform_data->device_runq, dev, links);
+               dev->flags &= ~AHC_DEV_ON_RUN_LIST;
+               ahc_linux_check_device_queue(ahc, dev);
+               /* Yeild to our interrupt handler */
+               ahc_unlock(ahc, &flags);
+               ahc_lock(ahc, &flags);
+       }
+       ahc_unlock(ahc, &flags);
+}
+
+/************************ Shutdown/halt/reboot hook ***************************/
+#include <xeno/notifier.h>
+#include <xeno/reboot.h>
+
+#if XENO_KILLED
+static struct notifier_block ahc_linux_notifier = {
+       ahc_linux_halt, NULL, 0
+};
+#endif
+
+static int ahc_linux_halt(struct notifier_block *nb, u_long event, void *buf)
+{
+       struct ahc_softc *ahc;
+
+       if (event == SYS_DOWN || event == SYS_HALT) {
+               TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+                       ahc_shutdown(ahc);
+               }
+       }
+       return (NOTIFY_OK);
+}
+
+/******************************** Macros **************************************/
+#define BUILD_SCSIID(ahc, cmd)                                         \
+       ((((cmd)->target << TID_SHIFT) & TID)                           \
+       | (((cmd)->channel == 0) ? (ahc)->our_id : (ahc)->our_id_b)     \
+       | (((cmd)->channel == 0) ? 0 : TWIN_CHNLB))
+
+/******************************** Bus DMA *************************************/
+int
+ahc_dma_tag_create(struct ahc_softc *ahc, bus_dma_tag_t parent,
+                  bus_size_t alignment, bus_size_t boundary,
+                  bus_addr_t lowaddr, bus_addr_t highaddr,
+                  bus_dma_filter_t *filter, void *filterarg,
+                  bus_size_t maxsize, int nsegments,
+                  bus_size_t maxsegsz, int flags, bus_dma_tag_t *ret_tag)
+{
+       bus_dma_tag_t dmat;
+
+       dmat = malloc(sizeof(*dmat), M_DEVBUF, M_NOWAIT);
+       if (dmat == NULL)
+               return (ENOMEM);
+
+       /*
+        * Linux is very simplistic about DMA memory.  For now don't
+        * maintain all specification information.  Once Linux supplies
+        * better facilities for doing these operations, or the
+        * needs of this particular driver change, we might need to do
+        * more here.
+        */
+       dmat->alignment = alignment;
+       dmat->boundary = boundary;
+       dmat->maxsize = maxsize;
+       *ret_tag = dmat;
+       return (0);
+}
+
+void
+ahc_dma_tag_destroy(struct ahc_softc *ahc, bus_dma_tag_t dmat)
+{
+       free(dmat, M_DEVBUF);
+}
+
+int
+ahc_dmamem_alloc(struct ahc_softc *ahc, bus_dma_tag_t dmat, void** vaddr,
+                int flags, bus_dmamap_t *mapp)
+{
+       bus_dmamap_t map;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT);
+       if (map == NULL)
+               return (ENOMEM);
+       /*
+        * Although we can dma data above 4GB, our
+        * "consistent" memory is below 4GB for
+        * space efficiency reasons (only need a 4byte
+        * address).  For this reason, we have to reset
+        * our dma mask when doing allocations.
+        */
+       if (ahc->dev_softc != NULL) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
+               pci_set_dma_mask(ahc->dev_softc, 0xFFFFFFFF);
+#else
+               ahc->dev_softc->dma_mask = 0xFFFFFFFF;
+#endif
+       }
+       *vaddr = pci_alloc_consistent(ahc->dev_softc,
+                                     dmat->maxsize, &map->bus_addr);
+       if (ahc->dev_softc != NULL) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
+               pci_set_dma_mask(ahc->dev_softc,
+                                ahc->platform_data->hw_dma_mask);
+#else
+               ahc->dev_softc->dma_mask = ahc->platform_data->hw_dma_mask;
+#endif
+       }
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) */
+       /*
+        * At least in 2.2.14, malloc is a slab allocator so all
+        * allocations are aligned.  We assume for these kernel versions
+        * that all allocations will be bellow 4Gig, physically contiguous,
+        * and accessable via DMA by the controller.
+        */
+       map = NULL; /* No additional information to store */
+       *vaddr = malloc(dmat->maxsize, M_DEVBUF, M_NOWAIT);
+#endif
+       if (*vaddr == NULL)
+               return (ENOMEM);
+       *mapp = map;
+       return(0);
+}
+
+void
+ahc_dmamem_free(struct ahc_softc *ahc, bus_dma_tag_t dmat,
+               void* vaddr, bus_dmamap_t map)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       pci_free_consistent(ahc->dev_softc, dmat->maxsize,
+                           vaddr, map->bus_addr);
+#else
+       free(vaddr, M_DEVBUF);
+#endif
+}
+
+int
+ahc_dmamap_load(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map,
+               void *buf, bus_size_t buflen, bus_dmamap_callback_t *cb,
+               void *cb_arg, int flags)
+{
+       /*
+        * Assume for now that this will only be used during
+        * initialization and not for per-transaction buffer mapping.
+        */
+       bus_dma_segment_t stack_sg;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       stack_sg.ds_addr = map->bus_addr;
+#else
+       stack_sg.ds_addr = VIRT_TO_BUS(buf);
+#endif
+       stack_sg.ds_len = dmat->maxsize;
+       cb(cb_arg, &stack_sg, /*nseg*/1, /*error*/0);
+       return (0);
+}
+
+void
+ahc_dmamap_destroy(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+       /*
+        * The map may is NULL in our < 2.3.X implementation.
+        */
+       if (map != NULL)
+               free(map, M_DEVBUF);
+}
+
+int
+ahc_dmamap_unload(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+       /* Nothing to do */
+       return (0);
+}
+
+/********************* Platform Dependent Functions ***************************/
+int
+ahc_softc_comp(struct ahc_softc *lahc, struct ahc_softc *rahc)
+{
+       int     value;
+       int     rvalue;
+       int     lvalue;
+
+       /*
+        * Under Linux, cards are ordered as follows:
+        *      1) VLB/EISA BIOS enabled devices sorted by BIOS address.
+        *      2) PCI devices with BIOS enabled sorted by bus/slot/func.
+        *      3) All remaining VLB/EISA devices sorted by ioport.
+        *      4) All remaining PCI devices sorted by bus/slot/func.
+        */
+       value = (lahc->flags & AHC_BIOS_ENABLED)
+             - (rahc->flags & AHC_BIOS_ENABLED);
+       if (value != 0)
+               /* Controllers with BIOS enabled have a *higher* priority */
+               return (-value);
+
+       /*
+        * Same BIOS setting, now sort based on bus type.
+        * EISA and VL controllers sort together.  EISA/VL
+        * have higher priority than PCI.
+        */
+       rvalue = (rahc->chip & AHC_BUS_MASK);
+       if (rvalue == AHC_VL)
+               rvalue = AHC_EISA;
+       lvalue = (lahc->chip & AHC_BUS_MASK);
+       if (lvalue == AHC_VL)
+               lvalue = AHC_EISA;
+       value = lvalue - rvalue;
+       if (value != 0)
+               return (value);
+
+       /* Still equal.  Sort by BIOS address, ioport, or bus/slot/func. */
+       switch (rvalue) {
+       case AHC_PCI:
+       {
+               char primary_channel;
+
+               if (aic7xxx_reverse_scan != 0)
+                       value = ahc_get_pci_bus(rahc->dev_softc)
+                             - ahc_get_pci_bus(lahc->dev_softc);
+               else
+                       value = ahc_get_pci_bus(lahc->dev_softc)
+                             - ahc_get_pci_bus(rahc->dev_softc);
+               if (value != 0)
+                       break;
+               if (aic7xxx_reverse_scan != 0)
+                       value = ahc_get_pci_slot(rahc->dev_softc)
+                             - ahc_get_pci_slot(lahc->dev_softc);
+               else
+                       value = ahc_get_pci_slot(lahc->dev_softc)
+                             - ahc_get_pci_slot(rahc->dev_softc);
+               if (value != 0)
+                       break;
+               /*
+                * On multi-function devices, the user can choose
+                * to have function 1 probed before function 0.
+                * Give whichever channel is the primary channel
+                * the lowest priority.
+                */
+               primary_channel = (lahc->flags & AHC_PRIMARY_CHANNEL) + 'A';
+               value = 1;
+               if (lahc->channel == primary_channel)
+                       value = -1;
+               break;
+       }
+       case AHC_EISA:
+               if ((rahc->flags & AHC_BIOS_ENABLED) != 0) {
+                       value = lahc->platform_data->bios_address
+                             - rahc->platform_data->bios_address; 
+               } else {
+                       value = lahc->bsh.ioport
+                             - rahc->bsh.ioport; 
+               }
+               break;
+       default:
+               panic("ahc_softc_sort: invalid bus type");
+       }
+       return (value);
+}
+
+static void
+ahc_linux_setup_tag_info(char *p, char *end)
+{
+       char    *base;
+       char    *tok;
+       char    *tok_end;
+       char    *tok_end2;
+       int      i;
+       int      instance;
+       int      targ;
+       int      done;
+       char     tok_list[] = {'.', ',', '{', '}', '\0'};
+
+       if (*p != ':')
+               return;
+
+       instance = -1;
+       targ = -1;
+       done = FALSE;
+       base = p;
+       /* Forward us just past the ':' */
+       tok = base + 1;
+       tok_end = strchr(tok, '\0');
+       if (tok_end < end)
+               *tok_end = ',';
+       while (!done) {
+               switch (*tok) {
+               case '{':
+                       if (instance == -1)
+                               instance = 0;
+                       else if (targ == -1)
+                               targ = 0;
+                       tok++;
+                       break;
+               case '}':
+                       if (targ != -1)
+                               targ = -1;
+                       else if (instance != -1)
+                               instance = -1;
+                       tok++;
+                       break;
+               case ',':
+               case '.':
+                       if (instance == -1)
+                               done = TRUE;
+                       else if (targ >= 0)
+                               targ++;
+                       else if (instance >= 0)
+                               instance++;
+                       if ((targ >= AHC_NUM_TARGETS) ||
+                           (instance >= NUM_ELEMENTS(aic7xxx_tag_info)))
+                               done = TRUE;
+                       tok++;
+                       if (!done) {
+                               base = tok;
+                       }
+                       break;
+               case '\0':
+                       done = TRUE;
+                       break;
+               default:
+                       done = TRUE;
+                       tok_end = strchr(tok, '\0');
+                       for (i = 0; tok_list[i]; i++) {
+                               tok_end2 = strchr(tok, tok_list[i]);
+                               if ((tok_end2) && (tok_end2 < tok_end)) {
+                                       tok_end = tok_end2;
+                                       done = FALSE;
+                               }
+                       }
+                       if ((instance >= 0) && (targ >= 0)
+                        && (instance < NUM_ELEMENTS(aic7xxx_tag_info))
+                        && (targ < AHC_NUM_TARGETS)) {
+                               aic7xxx_tag_info[instance].tag_commands[targ] =
+                                   simple_strtoul(tok, NULL, 0) & 0xff;
+                       }
+                       tok = tok_end;
+                       break;
+               }
+       }
+       while ((p != base) && (p != NULL))
+               p = strtok(NULL, ",.");
+}
+
+/*
+ * Handle Linux boot parameters. This routine allows for assigning a value
+ * to a parameter with a ':' between the parameter and the value.
+ * ie. aic7xxx=stpwlev:1,extended
+ */
+int
+aic7xxx_setup(char *s)
+{
+       int     i, n;
+       char   *p;
+       char   *end;
+
+       static struct {
+               const char *name;
+               uint32_t *flag;
+       } options[] = {
+               { "extended", &aic7xxx_extended },
+               { "no_reset", &aic7xxx_no_reset },
+               { "verbose", &aic7xxx_verbose },
+               { "reverse_scan", &aic7xxx_reverse_scan },
+               { "no_probe", &aic7xxx_no_probe },
+               { "periodic_otag", &aic7xxx_periodic_otag },
+               { "pci_parity", &aic7xxx_pci_parity },
+               { "seltime", &aic7xxx_seltime },
+               { "tag_info", NULL }
+       };
+
+       end = strchr(s, '\0');
+
+       for (p = strtok(s, ",."); p; p = strtok(NULL, ",.")) {
+               for (i = 0; i < NUM_ELEMENTS(options); i++) {
+                       n = strlen(options[i].name);
+
+                       if (strncmp(options[i].name, p, n) != 0)
+                               continue;
+
+                       if (strncmp(p, "tag_info", n) == 0) {
+                               ahc_linux_setup_tag_info(p + n, end);
+                       } else if (p[n] == ':') {
+                               *(options[i].flag) =
+                                   simple_strtoul(p + n + 1, NULL, 0);
+                       } else if (!strncmp(p, "verbose", n)) {
+                               *(options[i].flag) = 1;
+                       } else {
+                               *(options[i].flag) = ~(*(options[i].flag));
+                       }
+                       break;
+               }
+       }
+       return 1;
+}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
+__setup("aic7xxx=", aic7xxx_setup);
+#endif
+
+int aic7xxx_verbose;
+
+/*
+ * Try to detect an Adaptec 7XXX controller.
+ */
+int
+ahc_linux_detect(Scsi_Host_Template *template)
+{
+       struct  ahc_softc *ahc;
+       int     found;
+
+       /*
+        * It is a bug that the upper layer takes
+        * this lock just prior to calling us.
+        */
+       spin_unlock_irq(&io_request_lock);
+
+       /*
+        * Sanity checking of Linux SCSI data structures so
+        * that some of our hacks^H^H^H^H^Hassumptions aren't
+        * violated.
+        */
+       if (offsetof(struct ahc_cmd_internal, end)
+         > offsetof(struct scsi_cmnd, host_scribble)) {
+               printf("ahc_linux_detect: SCSI data structures changed.\n");
+               printf("ahc_linux_detect: Unable to attach\n");
+               return (0);
+       }
+#ifdef MODULE
+       /*
+        * If we've been passed any parameters, process them now.
+        */
+       if (aic7xxx)
+               aic7xxx_setup(aic7xxx);
+       if (dummy_buffer[0] != 'P')
+               printf(KERN_WARNING
+"aic7xxx: Please read the file /usr/src/xeno/drivers/scsi/README.aic7xxx\n"
+"aic7xxx: to see the proper way to specify options to the aic7xxx module\n"
+"aic7xxx: Specifically, don't use any commas when passing arguments to\n"
+"aic7xxx: insmod or else it might trash certain memory areas.\n");
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
+       template->proc_name = "aic7xxx";
+#else
+       template->proc_dir = &proc_scsi_aic7xxx;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,7)
+       /*
+        * We can only map 16MB per-SG
+        * so create a sector limit of
+        * "16MB" in 2K sectors.
+        */
+       template->max_sectors = 8192;
+#endif
+
+       /*
+        * Initialize our softc list lock prior to
+        * probing for any adapters.
+        */
+       ahc_list_lockinit();
+
+#ifdef CONFIG_PCI
+       ahc_linux_pci_probe(template);
+#endif
+
+       if (aic7xxx_no_probe == 0)
+               aic7770_linux_probe(template);
+
+       /*
+        * Register with the SCSI layer all
+        * controllers we've found.
+        */
+       spin_lock_irq(&io_request_lock);
+       found = 0;
+       TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+
+               if (ahc_linux_register_host(ahc, template) == 0)
+                       found++;
+       }
+       aic7xxx_detect_complete++;
+       return (found);
+}
+
+int
+ahc_linux_register_host(struct ahc_softc *ahc, Scsi_Host_Template *template)
+{
+       char  buf[80];
+       struct Scsi_Host *host;
+       char *new_name;
+       u_long s;
+
+
+       template->name = ahc->description;
+       host = scsi_register(template, sizeof(struct ahc_softc *));
+       if (host == NULL)
+               return (ENOMEM);
+
+       ahc_lock(ahc, &s);
+       *((struct ahc_softc **)host->hostdata) = ahc;
+       ahc->platform_data->host = host;
+       host->can_queue = AHC_MAX_QUEUE;
+       host->cmd_per_lun = 2;
+       host->sg_tablesize = AHC_NSEG;
+       host->select_queue_depths = ahc_linux_select_queue_depth;
+       /* XXX No way to communicate the ID for multiple channels */
+       host->this_id = ahc->our_id;
+       host->irq = ahc->platform_data->irq;
+       host->max_id = (ahc->features & AHC_WIDE) ? 16 : 8;
+       host->max_lun = AHC_NUM_LUNS;
+       host->max_channel = (ahc->features & AHC_TWIN) ? 1 : 0;
+       ahc_set_unit(ahc, ahc_linux_next_unit());
+       sprintf(buf, "scsi%d", host->host_no);
+       new_name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+       if (new_name != NULL) {
+               strcpy(new_name, buf);
+               ahc_set_name(ahc, new_name);
+       }
+       host->unique_id = ahc->unit;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)
+       scsi_set_pci_device(host, ahc->dev_softc);
+#endif
+       ahc_linux_initialize_scsi_bus(ahc);
+       ahc_unlock(ahc, &s);
+       return (0);
+}
+
+uint64_t
+ahc_linux_get_memsize()
+{
+  //   struct sysinfo si;
+  //
+  //   si_meminfo(&si);
+  //   return (si.totalram << PAGE_SHIFT);
+  printf("JWS: aic7xxx: get_memsize\n");
+  return 0;
+}
+
+/*
+ * Find the smallest available unit number to use
+ * for a new device.  We don't just use a static
+ * count to handle the "repeated hot-(un)plug"
+ * scenario.
+ */
+static int
+ahc_linux_next_unit()
+{
+       struct ahc_softc *ahc;
+       int unit;
+
+       unit = 0;
+retry:
+       TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+               if (ahc->unit == unit) {
+                       unit++;
+                       goto retry;
+               }
+       }
+       return (unit);
+}
+
+/*
+ * Place the SCSI bus into a known state by either resetting it,
+ * or forcing transfer negotiations on the next command to any
+ * target.
+ */
+void
+ahc_linux_initialize_scsi_bus(struct ahc_softc *ahc)
+{
+       int i;
+       int numtarg;
+
+       i = 0;
+       numtarg = 0;
+
+       if (aic7xxx_no_reset != 0)
+               ahc->flags &= ~(AHC_RESET_BUS_A|AHC_RESET_BUS_B);
+
+       if ((ahc->flags & AHC_RESET_BUS_A) != 0)
+               ahc_reset_channel(ahc, 'A', /*initiate_reset*/TRUE);
+       else
+               numtarg = (ahc->features & AHC_WIDE) ? 16 : 8;
+
+       if ((ahc->features & AHC_TWIN) != 0) {
+
+               if ((ahc->flags & AHC_RESET_BUS_B) != 0) {
+                       ahc_reset_channel(ahc, 'B', /*initiate_reset*/TRUE);
+               } else {
+                       if (numtarg == 0)
+                               i = 8;
+                       numtarg += 8;
+               }
+       }
+
+       for (; i < numtarg; i++) {
+               struct ahc_devinfo devinfo;
+               struct ahc_initiator_tinfo *tinfo;
+               struct ahc_tmode_tstate *tstate;
+               u_int our_id;
+               u_int target_id;
+               char channel;
+
+               channel = 'A';
+               our_id = ahc->our_id;
+               target_id = i;
+               if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+                       channel = 'B';
+                       our_id = ahc->our_id_b;
+                       target_id = i % 8;
+               }
+               tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+                                           target_id, &tstate);
+               tinfo->goal = tinfo->user;
+               /*
+                * Don't try negotiations that require PPR messages
+                * until we successfully retrieve Inquiry data.
+                */
+               tinfo->goal.ppr_options = 0;
+               if (tinfo->goal.transport_version > SCSI_REV_2)
+                       tinfo->goal.transport_version = SCSI_REV_2;
+               ahc_compile_devinfo(&devinfo, our_id, target_id,
+                                  CAM_LUN_WILDCARD, channel, ROLE_INITIATOR);
+               ahc_update_neg_request(ahc, &devinfo, tstate,
+                                      tinfo, /*force*/FALSE);
+       }
+       /* Give the bus some time to recover */
+       if ((ahc->flags & (AHC_RESET_BUS_A|AHC_RESET_BUS_B)) != 0) {      
+         // JWS - XEN - DONT USE TIMERS HERE
+#if 0
+               ahc_linux_freeze_sim_queue(ahc);
+               init_timer(&ahc->platform_data->reset_timer);
+               ahc->platform_data->reset_timer.data = (u_long)ahc;
+               ahc->platform_data->reset_timer.expires =
+                   jiffies + (AIC7XXX_RESET_DELAY * HZ)/1000;
+               ahc->platform_data->reset_timer.function =
+                   ahc_linux_release_sim_queue;
+               add_timer(&ahc->platform_data->reset_timer);
+#else
+         mdelay(AIC7XXX_RESET_DELAY);
+#endif   
+       }
+}
+
+int
+ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg)
+{
+       ahc->platform_data =
+           malloc(sizeof(struct ahc_platform_data), M_DEVBUF, M_NOWAIT);
+       if (ahc->platform_data == NULL)
+               return (ENOMEM);
+       memset(ahc->platform_data, 0, sizeof(struct ahc_platform_data));
+       TAILQ_INIT(&ahc->platform_data->completeq);
+       TAILQ_INIT(&ahc->platform_data->device_runq);
+       ahc->platform_data->irq = AHC_LINUX_NOIRQ;
+       ahc->platform_data->hw_dma_mask = 0xFFFFFFFF;
+       ahc_lockinit(ahc);
+       ahc_done_lockinit(ahc);
+#if 0
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       init_MUTEX_LOCKED(&ahc->platform_data->eh_sem);
+#else
+       ahc->platform_data->eh_sem = MUTEX_LOCKED;
+#endif
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+       tasklet_init(&ahc->platform_data->runq_tasklet, ahc_runq_tasklet,
+                    (unsigned long)ahc);
+#endif
+       ahc->seltime = (aic7xxx_seltime & 0x3) << 4;
+       ahc->seltime_b = (aic7xxx_seltime & 0x3) << 4;
+#if XENO_KILLED
+       if (TAILQ_EMPTY(&ahc_tailq))
+               register_reboot_notifier(&ahc_linux_notifier);
+#endif
+       return (0);
+}
+
+void
+ahc_platform_free(struct ahc_softc *ahc)
+{
+       if (ahc->platform_data != NULL) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+               tasklet_kill(&ahc->platform_data->runq_tasklet);
+#endif
+               if (ahc->platform_data->host != NULL)
+                       scsi_unregister(ahc->platform_data->host);
+               if (ahc->platform_data->irq != AHC_LINUX_NOIRQ)
+                       free_irq(ahc->platform_data->irq, ahc);
+               if (ahc->tag == BUS_SPACE_PIO
+                && ahc->bsh.ioport != 0)
+                       release_region(ahc->bsh.ioport, 256);
+               if (ahc->tag == BUS_SPACE_MEMIO
+                && ahc->bsh.maddr != NULL) {
+                       u_long base_addr;
+
+                       base_addr = (u_long)ahc->bsh.maddr;
+                       base_addr &= PAGE_MASK;
+                       iounmap((void *)base_addr);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+                       release_mem_region(ahc->platform_data->mem_busaddr,
+                                          0x1000);
+#endif
+               }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+               /* XXX Need an instance detach in the PCI code */
+               if (ahc->dev_softc != NULL)
+                       ahc->dev_softc->driver = NULL;
+#endif
+               free(ahc->platform_data, M_DEVBUF);
+       }
+       if (TAILQ_EMPTY(&ahc_tailq)) {
+#ifdef XENO_KILLED
+         unregister_reboot_notifier(&ahc_linux_notifier);
+#endif
+#ifdef CONFIG_PCI
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+               pci_unregister_driver(&aic7xxx_pci_driver);
+#endif
+#endif
+       }
+}
+
+void
+ahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
+{
+       ahc_platform_abort_scbs(ahc, SCB_GET_TARGET(ahc, scb),
+                               SCB_GET_CHANNEL(ahc, scb),
+                               SCB_GET_LUN(scb), SCB_LIST_NULL,
+                               ROLE_UNKNOWN, CAM_REQUEUE_REQ);
+}
+
+void
+ahc_platform_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+                     ahc_queue_alg alg)
+{
+       struct ahc_linux_device *dev;
+       int was_queuing;
+       int now_queuing;
+
+       dev = ahc_linux_get_device(ahc, devinfo->channel - 'A',
+                                  devinfo->target,
+                                  devinfo->lun, /*alloc*/FALSE);
+       if (dev == NULL)
+               return;
+       was_queuing = dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED);
+       now_queuing = alg != AHC_QUEUE_NONE;
+       if ((dev->flags & AHC_DEV_FREEZE_TIL_EMPTY) == 0
+        && (was_queuing != now_queuing)
+        && (dev->active != 0)) {
+               dev->flags |= AHC_DEV_FREEZE_TIL_EMPTY;
+               dev->qfrozen++;
+       }
+
+       dev->flags &= ~(AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED|AHC_DEV_PERIODIC_OTAG);
+       if (now_queuing) {
+               u_int usertags;
+
+               usertags = ahc_linux_user_tagdepth(ahc, devinfo);
+               if (!was_queuing) {
+                       /*
+                        * Start out agressively and allow our
+                        * dynamic queue depth algorithm to take
+                        * care of the rest.
+                        */
+                       dev->maxtags = usertags;
+                       dev->openings = dev->maxtags - dev->active;
+               }
+               if (alg == AHC_QUEUE_TAGGED) {
+                       dev->flags |= AHC_DEV_Q_TAGGED;
+                       if (aic7xxx_periodic_otag != 0)
+                               dev->flags |= AHC_DEV_PERIODIC_OTAG;
+               } else
+                       dev->flags |= AHC_DEV_Q_BASIC;
+       } else {
+               /* We can only have one opening. */
+               dev->maxtags = 0;
+               dev->openings =  1 - dev->active;
+       }
+}
+
+int
+ahc_platform_abort_scbs(struct ahc_softc *ahc, int target, char channel,
+                       int lun, u_int tag, role_t role, uint32_t status)
+{
+       int chan;
+       int maxchan;
+       int targ;
+       int maxtarg;
+       int clun;
+       int maxlun;
+       int count;
+
+       if (tag != SCB_LIST_NULL)
+               return (0);
+
+       chan = 0;
+       if (channel != ALL_CHANNELS) {
+               chan = channel - 'A';
+               maxchan = chan + 1;
+       } else {
+               maxchan = (ahc->features & AHC_TWIN) ? 2 : 1;
+       }
+       targ = 0;
+       if (target != CAM_TARGET_WILDCARD) {
+               targ = target;
+               maxtarg = targ + 1;
+       } else {
+               maxtarg = (ahc->features & AHC_WIDE) ? 16 : 8;
+       }
+       clun = 0;
+       if (lun != CAM_LUN_WILDCARD) {
+               clun = lun;
+               maxlun = clun + 1;
+       } else {
+               maxlun = AHC_NUM_LUNS;
+       }
+
+       count = 0;
+       for (; chan < maxchan; chan++) {
+
+               for (; targ < maxtarg; targ++) {
+
+                       for (; clun < maxlun; clun++) {
+                               struct ahc_linux_device *dev;
+                               struct ahc_busyq *busyq;
+                               struct ahc_cmd *acmd;
+
+                               dev = ahc_linux_get_device(ahc, chan,
+                                                          targ, clun,
+                                                          /*alloc*/FALSE);
+                               if (dev == NULL)
+                                       continue;
+
+                               busyq = &dev->busyq;
+                               while ((acmd = TAILQ_FIRST(busyq)) != NULL) {
+                                       Scsi_Cmnd *cmd;
+
+                                       cmd = &acmd_scsi_cmd(acmd);
+                                       TAILQ_REMOVE(busyq, acmd,
+                                                    acmd_links.tqe);
+                                       count++;
+                                       cmd->result = status << 16;
+                                       ahc_linux_queue_cmd_complete(ahc, cmd);
+                               }
+                       }
+               }
+       }
+
+       return (count);
+}
+
+/*
+ * Sets the queue depth for each SCSI device hanging
+ * off the input host adapter.
+ */
+static void
+ahc_linux_select_queue_depth(struct Scsi_Host * host,
+                            Scsi_Device * scsi_devs)
+{
+       Scsi_Device *device;
+       struct  ahc_softc *ahc;
+       u_long  flags;
+       int     scbnum;
+
+       ahc = *((struct ahc_softc **)host->hostdata);
+       ahc_lock(ahc, &flags);
+       scbnum = 0;
+       for (device = scsi_devs; device != NULL; device = device->next) {
+               if (device->host == host) {
+                       ahc_linux_device_queue_depth(ahc, device);
+                       scbnum += device->queue_depth;
+               }
+       }
+       ahc_unlock(ahc, &flags);
+}
+
+static u_int
+ahc_linux_user_tagdepth(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+       static int warned_user;
+       u_int tags;
+
+       tags = 0;
+       if ((ahc->user_discenable & devinfo->target_mask) != 0) {
+               if (warned_user == 0
+                && ahc->unit >= NUM_ELEMENTS(aic7xxx_tag_info)) {
+
+                       printf("aic7xxx: WARNING, insufficient "
+                              "tag_info instances for installed "
+                              "controllers. Using defaults\n");
+                       printf("aic7xxx: Please update the "
+                              "aic7xxx_tag_info array in the "
+                              "aic7xxx.c source file.\n");
+                       tags = AHC_MAX_QUEUE;
+                       warned_user++;
+               } else {
+                       adapter_tag_info_t *tag_info;
+
+                       tag_info = &aic7xxx_tag_info[ahc->unit];
+                       tags = tag_info->tag_commands[devinfo->target_offset];
+                       if (tags > AHC_MAX_QUEUE)
+                               tags = AHC_MAX_QUEUE;
+               }
+       }
+       return (tags);
+}
+
+/*
+ * Determines the queue depth for a given device.
+ */
+static void
+ahc_linux_device_queue_depth(struct ahc_softc *ahc, Scsi_Device * device)
+{
+       struct  ahc_devinfo devinfo;
+       u_int   tags;
+
+       ahc_compile_devinfo(&devinfo,
+                           device->channel == 0 ? ahc->our_id : ahc->our_id_b,
+                           device->id, device->lun,
+                           device->channel == 0 ? 'A' : 'B',
+                           ROLE_INITIATOR);
+       tags = ahc_linux_user_tagdepth(ahc, &devinfo);
+       if (tags != 0
+        && device->tagged_supported != 0) {
+
+               device->queue_depth = tags;
+               ahc_set_tags(ahc, &devinfo, AHC_QUEUE_TAGGED);
+               printf("scsi%d:%c:%d:%d: Tagged Queuing enabled.  Depth %d\n",
+                      ahc->platform_data->host->host_no, devinfo.channel,
+                      devinfo.target, devinfo.lun, tags);
+       } else {
+               /*
+                * We allow the OS to queue 2 untagged transactions to
+                * us at any time even though we can only execute them
+                * serially on the controller/device.  This should remove
+                * some latency.
+                */
+               device->queue_depth = 2;
+       }
+}
+
+/*
+ * Queue an SCB to the controller.
+ */
+int
+ahc_linux_queue(Scsi_Cmnd * cmd, void (*scsi_done) (Scsi_Cmnd *))
+{
+       struct   ahc_softc *ahc;
+       struct   ahc_linux_device *dev;
+       u_long   flags;
+
+       ahc = *(struct ahc_softc **)cmd->host->hostdata;
+
+       /*
+        * Save the callback on completion function.
+        */
+       cmd->scsi_done = scsi_done;
+
+       ahc_lock(ahc, &flags);
+       dev = ahc_linux_get_device(ahc, cmd->channel, cmd->target,
+                                  cmd->lun, /*alloc*/TRUE);
+       if (dev == NULL) {
+               ahc_unlock(ahc, &flags);
+               printf("aic7xxx_linux_queue: Unable to allocate device!\n");
+               return (-ENOMEM);
+       }
+       cmd->result = CAM_REQ_INPROG << 16;
+       TAILQ_INSERT_TAIL(&dev->busyq, (struct ahc_cmd *)cmd, acmd_links.tqe);
+       if ((dev->flags & AHC_DEV_ON_RUN_LIST) == 0) {
+               TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq, dev, links);
+               dev->flags |= AHC_DEV_ON_RUN_LIST;
+               ahc_linux_run_device_queues(ahc);
+       }
+       ahc_unlock(ahc, &flags);
+       return (0);
+}
+
+static void
+ahc_linux_run_device_queue(struct ahc_softc *ahc, struct ahc_linux_device *dev)
+{
+       struct   ahc_cmd *acmd;
+       struct   scsi_cmnd *cmd;
+       struct   scb *scb;
+       struct   hardware_scb *hscb;
+       struct   ahc_initiator_tinfo *tinfo;
+       struct   ahc_tmode_tstate *tstate;
+       uint16_t mask;
+
+       if ((dev->flags & AHC_DEV_ON_RUN_LIST) != 0)
+               panic("running device on run list");
+
+       while ((acmd = TAILQ_FIRST(&dev->busyq)) != NULL
+           && dev->openings > 0 && dev->qfrozen == 0) {
+
+               /*
+                * Schedule us to run later.  The only reason we are not
+                * running is because the whole controller Q is frozen.
+                */
+               if (ahc->platform_data->qfrozen != 0) {
+
+                       TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq,
+                                         dev, links);
+                       dev->flags |= AHC_DEV_ON_RUN_LIST;
+                       return;
+               }
+               /*
+                * Get an scb to use.
+                */
+               if ((scb = ahc_get_scb(ahc)) == NULL) {
+                       TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq,
+                                        dev, links);
+                       dev->flags |= AHC_DEV_ON_RUN_LIST;
+                       ahc->flags |= AHC_RESOURCE_SHORTAGE;
+                       return;
+               }
+               TAILQ_REMOVE(&dev->busyq, acmd, acmd_links.tqe);
+               cmd = &acmd_scsi_cmd(acmd);
+               scb->io_ctx = cmd;
+               scb->platform_data->dev = dev;
+               hscb = scb->hscb;
+               cmd->host_scribble = (char *)scb;
+
+               /*
+                * Fill out basics of the HSCB.
+                */
+               hscb->control = 0;
+               hscb->scsiid = BUILD_SCSIID(ahc, cmd);
+               hscb->lun = cmd->lun;
+               mask = SCB_GET_TARGET_MASK(ahc, scb);
+               tinfo = ahc_fetch_transinfo(ahc, SCB_GET_CHANNEL(ahc, scb),
+                                           SCB_GET_OUR_ID(scb),
+                                           SCB_GET_TARGET(ahc, scb), &tstate);
+               hscb->scsirate = tinfo->scsirate;
+               hscb->scsioffset = tinfo->curr.offset;
+               if ((tstate->ultraenb & mask) != 0)
+                       hscb->control |= ULTRAENB;
+
+               if ((ahc->user_discenable & mask) != 0)
+                       hscb->control |= DISCENB;
+
+               if ((tstate->auto_negotiate & mask) != 0) {
+                       scb->flags |= SCB_AUTO_NEGOTIATE;
+                       scb->hscb->control |= MK_MESSAGE;
+               }
+
+               if ((dev->flags & (AHC_DEV_Q_TAGGED|AHC_DEV_Q_BASIC)) != 0) {
+                       if (dev->commands_since_idle_or_otag == AHC_OTAG_THRESH
+                        && (dev->flags & AHC_DEV_Q_TAGGED) != 0) {
+                               hscb->control |= MSG_ORDERED_TASK;
+                               dev->commands_since_idle_or_otag = 0;
+                       } else {
+                               hscb->control |= MSG_SIMPLE_TASK;
+                       }
+               }
+
+               hscb->cdb_len = cmd->cmd_len;
+               if (hscb->cdb_len <= 12) {
+                       memcpy(hscb->shared_data.cdb, cmd->cmnd, hscb->cdb_len);
+               } else {
+                       memcpy(hscb->cdb32, cmd->cmnd, hscb->cdb_len);
+                       scb->flags |= SCB_CDB32_PTR;
+               }
+
+               scb->platform_data->xfer_len = 0;
+               ahc_set_residual(scb, 0);
+               ahc_set_sense_residual(scb, 0);
+               scb->sg_count = 0;
+               if (cmd->use_sg != 0) {
+                       struct  ahc_dma_seg *sg;
+                       struct  scatterlist *cur_seg;
+                       struct  scatterlist *end_seg;
+                       int     nseg;
+
+                       cur_seg = (struct scatterlist *)cmd->request_buffer;
+                       nseg = pci_map_sg(ahc->dev_softc, cur_seg, cmd->use_sg,
+                                scsi_to_pci_dma_dir(cmd ->sc_data_direction));
+                       end_seg = cur_seg + nseg;
+                       /* Copy the segments into the SG list. */
+                       sg = scb->sg_list;
+                       /*
+                        * The sg_count may be larger than nseg if
+                        * a transfer crosses a 32bit page.
+                        */ 
+                       while (cur_seg < end_seg) {
+                               bus_addr_t addr;
+                               bus_size_t len;
+                               int consumed;
+
+                               addr = sg_dma_address(cur_seg);
+                               len = sg_dma_len(cur_seg);
+                               consumed = ahc_linux_map_seg(ahc, scb,
+                                                            sg, addr, len);
+                               sg += consumed;
+                               scb->sg_count += consumed;
+                               cur_seg++;
+                       }
+                       sg--;
+                       sg->len |= ahc_htole32(AHC_DMA_LAST_SEG);
+
+                       /*
+                        * Reset the sg list pointer.
+                        */
+                       scb->hscb->sgptr =
+                           ahc_htole32(scb->sg_list_phys | SG_FULL_RESID);
+
+                       /*
+                        * Copy the first SG into the "current"
+                        * data pointer area.
+                        */
+                       scb->hscb->dataptr = scb->sg_list->addr;
+                       scb->hscb->datacnt = scb->sg_list->len;
+               } else if (cmd->request_bufflen != 0) {
+                       struct   ahc_dma_seg *sg;
+                       bus_addr_t addr;
+
+                       sg = scb->sg_list;
+                       addr = pci_map_single(ahc->dev_softc,
+                              cmd->request_buffer,
+                              cmd->request_bufflen,
+                              scsi_to_pci_dma_dir(cmd->sc_data_direction));
+                       scb->platform_data->buf_busaddr = addr;
+                       scb->sg_count = ahc_linux_map_seg(ahc, scb,
+                                                         sg, addr,
+                                                         cmd->request_bufflen);
+                       sg->len |= ahc_htole32(AHC_DMA_LAST_SEG);
+
+                       /*
+                        * Reset the sg list pointer.
+                        */
+                       scb->hscb->sgptr =
+                           ahc_htole32(scb->sg_list_phys | SG_FULL_RESID);
+
+                       /*
+                        * Copy the first SG into the "current"
+                        * data pointer area.
+                        */
+                       scb->hscb->dataptr = sg->addr;
+                       scb->hscb->datacnt = sg->len;
+               } else {
+                       scb->hscb->sgptr = ahc_htole32(SG_LIST_NULL);
+                       scb->hscb->dataptr = 0;
+                       scb->hscb->datacnt = 0;
+                       scb->sg_count = 0;
+               }
+
+               ahc_sync_sglist(ahc, scb, BUS_DMASYNC_PREWRITE);
+               LIST_INSERT_HEAD(&ahc->pending_scbs, scb, pending_links);
+               dev->openings--;
+               dev->active++;
+               dev->commands_issued++;
+               if ((dev->flags & AHC_DEV_PERIODIC_OTAG) != 0)
+                       dev->commands_since_idle_or_otag++;
+
+               /*
+                * We only allow one untagged transaction
+                * per target in the initiator role unless
+                * we are storing a full busy target *lun*
+                * table in SCB space.
+                */
+               if ((scb->hscb->control & (TARGET_SCB|TAG_ENB)) == 0
+                && (ahc->features & AHC_SCB_BTT) == 0) {
+                       struct scb_tailq *untagged_q;
+                       int target_offset;
+
+                       target_offset = SCB_GET_TARGET_OFFSET(ahc, scb);
+                       untagged_q = &(ahc->untagged_queues[target_offset]);
+                       TAILQ_INSERT_TAIL(untagged_q, scb, links.tqe);
+                       scb->flags |= SCB_UNTAGGEDQ;
+                       if (TAILQ_FIRST(untagged_q) != scb)
+                               continue;
+               }
+               scb->flags |= SCB_ACTIVE;
+               ahc_queue_scb(ahc, scb);
+       }
+}
+
+/*
+ * SCSI controller interrupt handler.
+ */
+void
+ahc_linux_isr(int irq, void *dev_id, struct pt_regs * regs)
+{
+       struct  ahc_softc *ahc;
+       struct  ahc_cmd *acmd;
+       u_long  flags;
+       struct  ahc_linux_device *next_dev;
+
+       ahc = (struct ahc_softc *) dev_id;
+       ahc_lock(ahc, &flags); 
+       ahc_intr(ahc);
+       acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+       TAILQ_INIT(&ahc->platform_data->completeq);
+       next_dev = ahc_linux_next_device_to_run(ahc);
+       ahc_unlock(ahc, &flags);
+       if (next_dev) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+               tasklet_schedule(&ahc->platform_data->runq_tasklet);
+               do_softirq();
+#else
+               ahc_runq_tasklet((unsigned long)ahc);
+#endif
+       }
+       if (acmd != NULL)
+               ahc_linux_run_complete_queue(ahc, acmd);
+}
+
+void
+ahc_platform_flushwork(struct ahc_softc *ahc)
+{
+       struct ahc_cmd *acmd;
+
+       acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+       TAILQ_INIT(&ahc->platform_data->completeq);
+       if (acmd != NULL)
+               ahc_linux_run_complete_queue(ahc, acmd);
+}
+
+static struct ahc_linux_target*
+ahc_linux_alloc_target(struct ahc_softc *ahc, u_int channel, u_int target)
+{
+       struct ahc_linux_target *targ;
+       u_int target_offset;
+
+       targ = malloc(sizeof(*targ), M_DEVBUG, M_NOWAIT);
+       if (targ == NULL)
+               return (NULL);
+       memset(targ, 0, sizeof(*targ));
+       targ->channel = channel;
+       targ->target = target;
+       targ->ahc = ahc;
+       target_offset = target;
+       if (channel != 0)
+               target_offset += 8;
+       ahc->platform_data->targets[target_offset] = targ;
+       return (targ);
+}
+
+static void
+ahc_linux_free_target(struct ahc_softc *ahc, struct ahc_linux_target *targ)
+{
+       u_int target_offset;
+
+       target_offset = targ->target;
+       if (targ->channel != 0)
+               target_offset += 8;
+       ahc->platform_data->targets[target_offset] = NULL;
+       free(targ, M_DEVBUF);
+}
+
+static struct ahc_linux_device*
+ahc_linux_alloc_device(struct ahc_softc *ahc,
+                struct ahc_linux_target *targ, u_int lun)
+{
+       struct ahc_linux_device *dev;
+
+       dev = malloc(sizeof(*dev), M_DEVBUG, M_NOWAIT);
+       if (dev == NULL)
+               return (NULL);
+       memset(dev, 0, sizeof(*dev));
+       init_timer(&dev->timer);
+       TAILQ_INIT(&dev->busyq);
+       dev->flags = AHC_DEV_UNCONFIGURED;
+       dev->lun = lun;
+       dev->target = targ;
+
+       /*
+        * We start out life using untagged
+        * transactions of which we allow one.
+        */
+       dev->openings = 1;
+
+       /*
+        * Set maxtags to 0.  This will be changed if we
+        * later determine that we are dealing with
+        * a tagged queuing capable device.
+        */
+       dev->maxtags = 0;
+       
+       targ->refcount++;
+       targ->devices[lun] = dev;
+       return (dev);
+}
+
+static void
+ahc_linux_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev)
+{
+       struct ahc_linux_target *targ;
+
+       del_timer(&dev->timer);
+       targ = dev->target;
+       targ->devices[dev->lun] = NULL;
+       free(dev, M_DEVBUF);
+       targ->refcount--;
+       if (targ->refcount == 0)
+               ahc_linux_free_target(ahc, targ);
+}
+
+/*
+ * Return a string describing the driver.
+ */
+const char *
+ahc_linux_info(struct Scsi_Host *host)
+{
+       static char buffer[512];
+       char    ahc_info[256];
+       char   *bp;
+       struct ahc_softc *ahc;
+
+       bp = &buffer[0];
+       ahc = *(struct ahc_softc **)host->hostdata;
+       memset(bp, 0, sizeof(buffer));
+       strcpy(bp, "Adaptec AIC7XXX EISA/VLB/PCI SCSI HBA DRIVER, Rev ");
+       strcat(bp, AIC7XXX_DRIVER_VERSION);
+       strcat(bp, "\n");
+       strcat(bp, "        <");
+       strcat(bp, ahc->description);
+       strcat(bp, ">\n");
+       strcat(bp, "        ");
+       ahc_controller_info(ahc, ahc_info);
+       strcat(bp, ahc_info);
+       strcat(bp, "\n");
+
+       return (bp);
+}
+
+void
+ahc_send_async(struct ahc_softc *ahc, char channel,
+              u_int target, u_int lun, ac_code code, void *arg)
+{
+       switch (code) {
+       case AC_TRANSFER_NEG:
+       {
+               char    buf[80];
+               struct  ahc_linux_target *targ;
+               struct  info_str info;
+               struct  ahc_initiator_tinfo *tinfo;
+               struct  ahc_tmode_tstate *tstate;
+               int     target_offset;
+
+               info.buffer = buf;
+               info.length = sizeof(buf);
+               info.offset = 0;
+               info.pos = 0;
+               tinfo = ahc_fetch_transinfo(ahc, channel,
+                                               channel == 'A' ? ahc->our_id
+                                                              : ahc->our_id_b,
+                                               target, &tstate);
+
+               /*
+                * Don't bother reporting results while
+                * negotiations are still pending.
+                */
+               if (tinfo->curr.period != tinfo->goal.period
+                || tinfo->curr.width != tinfo->goal.width
+                || tinfo->curr.offset != tinfo->goal.offset
+                || tinfo->curr.ppr_options != tinfo->goal.ppr_options)
+                       if (bootverbose == 0)
+                               break;
+
+               /*
+                * Don't bother reporting results that
+                * are identical to those last reported.
+                */
+               target_offset = target;
+               if (channel == 'B')
+                       target_offset += 8;
+               targ = ahc->platform_data->targets[target_offset];
+               if (targ == NULL)
+                       break;
+               if (tinfo->curr.period == targ->last_tinfo.period
+                && tinfo->curr.width == targ->last_tinfo.width
+                && tinfo->curr.offset == targ->last_tinfo.offset
+                && tinfo->curr.ppr_options == targ->last_tinfo.ppr_options)
+                       if (bootverbose == 0)
+                               break;
+
+               targ->last_tinfo.period = tinfo->curr.period;
+               targ->last_tinfo.width = tinfo->curr.width;
+               targ->last_tinfo.offset = tinfo->curr.offset;
+               targ->last_tinfo.ppr_options = tinfo->curr.ppr_options;
+
+               printf("(%s:%c:", ahc_name(ahc), channel);
+               if (target == CAM_TARGET_WILDCARD)
+                       printf("*): ");
+               else
+                       printf("%d): ", target);
+               ahc_format_transinfo(&info, &tinfo->curr);
+               if (info.pos < info.length)
+                       *info.buffer = '\0';
+               else
+                       buf[info.length - 1] = '\0';
+               printf("%s", buf);
+               break;
+       }
+        case AC_SENT_BDR:
+               break;
+        case AC_BUS_RESET:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+               if (ahc->platform_data->host != NULL) {
+                       scsi_report_bus_reset(ahc->platform_data->host,
+                                             channel - 'A');
+               }
+#endif
+                break;
+        default:
+                panic("ahc_send_async: Unexpected async event");
+        }
+}
+
+/*
+ * Calls the higher level scsi done function and frees the scb.
+ */
+void
+ahc_done(struct ahc_softc *ahc, struct scb * scb)
+{
+       Scsi_Cmnd *cmd;
+       struct ahc_linux_device *dev;
+
+       LIST_REMOVE(scb, pending_links);
+       if ((scb->flags & SCB_UNTAGGEDQ) != 0) {
+               struct scb_tailq *untagged_q;
+               int target_offset;
+
+               target_offset = SCB_GET_TARGET_OFFSET(ahc, scb);
+               untagged_q = &(ahc->untagged_queues[target_offset]);
+               TAILQ_REMOVE(untagged_q, scb, links.tqe);
+               ahc_run_untagged_queue(ahc, untagged_q);
+       }
+
+       if ((scb->flags & SCB_ACTIVE) == 0) {
+               printf("SCB %d done'd twice\n", scb->hscb->tag);
+               ahc_dump_card_state(ahc);
+               panic("Stopping for safety");
+       }
+       cmd = scb->io_ctx;
+       dev = scb->platform_data->dev;
+       dev->active--;
+       dev->openings++;
+       ahc_linux_unmap_scb(ahc, scb);
+       if (scb->flags & SCB_SENSE) {
+               memcpy(cmd->sense_buffer, ahc_get_sense_buf(ahc, scb),
+                      MIN(sizeof(struct scsi_sense_data),
+                          sizeof(cmd->sense_buffer)));
+               cmd->result |= (DRIVER_SENSE << 24);
+       } else {
+               /*
+                * Guard against stale sense data.
+                * The Linux mid-layer assumes that sense
+                * was retrieved anytime the first byte of
+                * the sense buffer looks "sane".
+                */
+               cmd->sense_buffer[0] = 0;
+       }
+       if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG) {
+               uint32_t amount_xferred;
+
+               amount_xferred =
+                   ahc_get_transfer_length(scb) - ahc_get_residual(scb);
+               if (amount_xferred < scb->io_ctx->underflow) {
+                       printf("Saw underflow (%ld of %ld bytes). "
+                              "Treated as error\n",
+                               ahc_get_residual(scb),
+                               ahc_get_transfer_length(scb));
+                       ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+               } else {
+                       ahc_set_transaction_status(scb, CAM_REQ_CMP);
+                       ahc_linux_sniff_command(ahc, cmd, scb);
+               }
+       } else if (ahc_get_transaction_status(scb) == DID_OK) {
+               ahc_linux_handle_scsi_status(ahc, dev, scb);
+       } else if (ahc_get_transaction_status(scb) == DID_NO_CONNECT) {
+               /*
+                * Should a selection timeout kill the device?
+                * That depends on whether the selection timeout
+                * is persistent.  Since we have no guarantee that
+                * the mid-layer will issue an inquiry for this device
+                * again, we can't just kill it off.
+               dev->flags |= AHC_DEV_UNCONFIGURED;
+                */
+       }
+
+       if (dev->openings == 1
+        && ahc_get_transaction_status(scb) == CAM_REQ_CMP
+        && ahc_get_scsi_status(scb) != SCSI_STATUS_QUEUE_FULL)
+               dev->tag_success_count++;
+       /*
+        * Some devices deal with temporary internal resource
+        * shortages by returning queue full.  When the queue
+        * full occurrs, we throttle back.  Slowly try to get
+        * back to our previous queue depth.
+        */
+       if ((dev->openings + dev->active) < dev->maxtags
+        && dev->tag_success_count > AHC_TAG_SUCCESS_INTERVAL) {
+               dev->tag_success_count = 0;
+               dev->openings++;
+       }
+
+       if (dev->active == 0)
+               dev->commands_since_idle_or_otag = 0;
+
+       if (TAILQ_EMPTY(&dev->busyq)) {
+               if ((dev->flags & AHC_DEV_UNCONFIGURED) != 0
+                && dev->active == 0)
+                       ahc_linux_free_device(ahc, dev);
+       } else if ((dev->flags & AHC_DEV_ON_RUN_LIST) == 0) {
+               TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq, dev, links);
+               dev->flags |= AHC_DEV_ON_RUN_LIST;
+       }
+
+       if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
+               printf("Recovery SCB completes\n");
+#if 0
+               up(&ahc->platform_data->eh_sem);
+#endif
+       }
+
+       ahc_free_scb(ahc, scb);
+       ahc_linux_queue_cmd_complete(ahc, cmd);
+}
+
+static void
+ahc_linux_handle_scsi_status(struct ahc_softc *ahc,
+                            struct ahc_linux_device *dev, struct scb *scb)
+{
+       /*
+        * We don't currently trust the mid-layer to
+        * properly deal with queue full or busy.  So,
+        * when one occurs, we tell the mid-layer to
+        * unconditionally requeue the command to us
+        * so that we can retry it ourselves.  We also
+        * implement our own throttling mechanism so
+        * we don't clobber the device with too many
+        * commands.
+        */
+       switch (ahc_get_scsi_status(scb)) {
+       default:
+               break;
+       case SCSI_STATUS_QUEUE_FULL:
+       {
+               /*
+                * By the time the core driver has returned this
+                * command, all other commands that were queued
+                * to us but not the device have been returned.
+                * This ensures that dev->active is equal to
+                * the number of commands actually queued to
+                * the device.
+                */
+               dev->tag_success_count = 0;
+               if (dev->active != 0) {
+                       /*
+                        * Drop our opening count to the number
+                        * of commands currently outstanding.
+                        */
+                       dev->openings = 0;
+/*
+                       ahc_print_path(ahc, scb);
+                       printf("Dropping tag count to %d\n", dev->active);
+ */
+                       if (dev->active == dev->tags_on_last_queuefull) {
+
+                               dev->last_queuefull_same_count++;
+                               /*
+                                * If we repeatedly see a queue full
+                                * at the same queue depth, this
+                                * device has a fixed number of tag
+                                * slots.  Lock in this tag depth
+                                * so we stop seeing queue fulls from
+                                * this device.
+                                */
+                               if (dev->last_queuefull_same_count
+                                == AHC_LOCK_TAGS_COUNT) {
+                                       dev->maxtags = dev->active;
+                                       ahc_print_path(ahc, scb);
+                                       printf("Locking max tag count at %d\n",
+                                              dev->active);
+                               }
+                       } else {
+                               dev->tags_on_last_queuefull = dev->active;
+                               dev->last_queuefull_same_count = 0;
+                       }
+                       ahc_set_transaction_status(scb, CAM_REQUEUE_REQ);
+                       ahc_set_scsi_status(scb, SCSI_STATUS_OK);
+                       break;
+               }
+               /*
+                * Drop down to a single opening, and treat this
+                * as if the target return BUSY SCSI status.
+                */
+               dev->openings = 1;
+               /* FALLTHROUGH */
+       }
+       case SCSI_STATUS_BUSY:
+       {
+               /*
+                * Set a short timer to defer sending commands for
+                * a bit since Linux will not delay in this case.
+                */
+               if ((dev->flags & AHC_DEV_TIMER_ACTIVE) != 0) {
+                       printf("%s:%c:%d: Device Timer still active during "
+                              "busy processing\n", ahc_name(ahc),
+                               dev->target->channel, dev->target->target);
+                       break;
+               }
+               dev->flags |= AHC_DEV_TIMER_ACTIVE;
+               dev->qfrozen++;
+               init_timer(&dev->timer);
+               dev->timer.data = (u_long)dev;
+               dev->timer.expires = jiffies + (HZ/2);
+               dev->timer.function = ahc_linux_dev_timed_unfreeze;
+               add_timer(&dev->timer);
+               break;
+       }
+       }
+}
+
+static void
+ahc_linux_filter_command(struct ahc_softc *ahc, Scsi_Cmnd *cmd, struct scb *scb)
+{
+       switch (cmd->cmnd[0]) {
+       case INQUIRY:
+       {
+               struct  ahc_devinfo devinfo;
+               struct  scsi_inquiry *inq;
+               struct  scsi_inquiry_data *sid;
+               struct  ahc_initiator_tinfo *tinfo;
+               struct  ahc_transinfo *user;
+               struct  ahc_transinfo *goal;
+               struct  ahc_transinfo *curr;
+               struct  ahc_tmode_tstate *tstate;
+               struct  ahc_syncrate *syncrate;
+               struct  ahc_linux_device *dev;
+               u_int   scsiid;
+               u_int   maxsync;
+               int     transferred_len;
+               int     minlen;
+               u_int   width;
+               u_int   period;
+               u_int   offset;
+               u_int   ppr_options;
+
+                /*
+                 * Validate the command.  We only want to filter
+                 * standard inquiry commands, not those querying
+                 * Vital Product Data.
+                 */
+               inq = (struct scsi_inquiry *)cmd->cmnd;
+               if ((inq->byte2 & SI_EVPD) != 0
+                || inq->page_code != 0)
+                       break;
+
+               if (cmd->use_sg != 0) {
+                       printf("%s: SG Inquiry response ignored\n",
+                              ahc_name(ahc));
+                       break;
+               }
+               transferred_len = ahc_get_transfer_length(scb)
+                               - ahc_get_residual(scb);
+               sid = (struct scsi_inquiry_data *)cmd->request_buffer;
+
+               /*
+                * Determine if this lun actually exists.  If so,
+                * hold on to its corresponding device structure.
+                * If not, make sure we release the device and
+                * don't bother processing the rest of this inquiry
+                * command.
+                */
+               dev = ahc_linux_get_device(ahc, cmd->channel,
+                                          cmd->target, cmd->lun,
+                                          /*alloc*/FALSE);
+               if (transferred_len >= 1
+                && SID_QUAL(sid) == SID_QUAL_LU_CONNECTED) {
+
+                       dev->flags &= ~AHC_DEV_UNCONFIGURED;
+               } else {
+                       dev->flags |= AHC_DEV_UNCONFIGURED;
+                       break;
+               }
+
+               /*
+                * Update our notion of this device's transfer
+                * negotiation capabilities.
+                */
+               scsiid = BUILD_SCSIID(ahc, cmd);
+               ahc_compile_devinfo(&devinfo, SCSIID_OUR_ID(scsiid),
+                                   cmd->target, cmd->lun,
+                                   SCSIID_CHANNEL(ahc, scsiid),
+                                   ROLE_INITIATOR);
+               tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
+                                           devinfo.our_scsiid,
+                                           devinfo.target, &tstate);
+               user = &tinfo->user;
+               goal = &tinfo->goal;
+               curr = &tinfo->curr;
+               width = user->width;
+               period = user->period;
+               offset = user->offset;
+               ppr_options = user->ppr_options;
+               minlen = offsetof(struct scsi_inquiry_data, version) + 1;
+               if (transferred_len >= minlen) {
+                       curr->protocol_version = SID_ANSI_REV(sid);
+
+                       /*
+                        * Only attempt SPI3 once we've verified that
+                        * the device claims to support SPI3 features.
+                        */
+                       if (curr->protocol_version < SCSI_REV_2)
+                               curr->transport_version = SID_ANSI_REV(sid);
+                       else
+                               curr->transport_version = SCSI_REV_2;
+               }
+
+               minlen = offsetof(struct scsi_inquiry_data, flags) + 1;
+               if (transferred_len >= minlen
+                && (sid->additional_length + 4) >= minlen) {
+                       if ((sid->flags & SID_WBus16) == 0)
+                               width = MSG_EXT_WDTR_BUS_8_BIT;
+                       if ((sid->flags & SID_Sync) == 0) {
+                               period = 0;
+                               offset = 0;
+                               ppr_options = 0;
+                       }
+               } else {
+                       /* Keep current settings */
+                       break;
+               }
+               minlen = offsetof(struct scsi_inquiry_data, spi3data) + 1;
+               /*
+                * This is a kludge to deal with inquiry requests that
+                * are not large enough for us to pull the spi3/4 bits.
+                * In this case, we assume that a device that tells us
+                * they can provide inquiry data that spans the SPI3
+                * bits and says its SCSI3 can handle a PPR request.
+                * If the inquiry request has sufficient buffer space to
+                * cover SPI3 bits, we honor them regardless of reported
+                * SCSI REV.  We also allow any device that has had its
+                * goal ppr_options set to allow DT speeds to keep that
+                * option if a short inquiry occurs that would fail the
+                * normal tests outlined above.
+                */
+               if ((sid->additional_length + 4) >= minlen) {
+                       if (transferred_len >= minlen) {
+                                if ((sid->spi3data & SID_SPI_CLOCK_DT) == 0)
+                                       ppr_options = 0;
+                       } else if ((goal->ppr_options & MSG_EXT_PPR_DT_REQ)== 0)
+                               ppr_options = 0;
+
+                       if (curr->protocol_version > SCSI_REV_2)
+                               curr->transport_version = 3;
+               } else {
+                       ppr_options = 0;
+               }
+               ahc_validate_width(ahc, /*tinfo limit*/NULL, &width,
+                                  ROLE_UNKNOWN);
+               if ((ahc->features & AHC_ULTRA2) != 0)
+                       maxsync = AHC_SYNCRATE_DT;
+               else if ((ahc->features & AHC_ULTRA) != 0)
+                       maxsync = AHC_SYNCRATE_ULTRA;
+               else
+                       maxsync = AHC_SYNCRATE_FAST;
+
+               syncrate = ahc_find_syncrate(ahc, &period,
+                                            &ppr_options, maxsync);
+               ahc_validate_offset(ahc, /*tinfo limit*/NULL, syncrate,
+                                   &offset, width, ROLE_UNKNOWN);
+               if (offset == 0 || period == 0) {
+                       period = 0;
+                       offset = 0;
+                       ppr_options = 0;
+               }
+               /* Apply our filtered user settings. */
+               ahc_set_width(ahc, &devinfo, width,
+                             AHC_TRANS_GOAL, /*paused*/FALSE);
+               ahc_set_syncrate(ahc, &devinfo, syncrate, period,
+                                offset, ppr_options, AHC_TRANS_GOAL,
+                                /*paused*/FALSE);
+               break;
+       }
+       default:
+               panic("ahc_linux_filter_command: Unexpected Command type  %x\n",
+                     cmd->cmnd[0]);
+               break;
+       }
+}
+
+#if 0
+static void
+ahc_linux_sem_timeout(u_long arg)
+{
+       struct semaphore *sem;
+
+       sem = (struct semaphore *)arg;
+       up(sem);
+}
+#endif
+
+static void
+ahc_linux_freeze_sim_queue(struct ahc_softc *ahc)
+{
+       ahc->platform_data->qfrozen++;
+       if (ahc->platform_data->qfrozen == 1)
+               scsi_block_requests(ahc->platform_data->host);
+}
+
+static void
+ahc_linux_release_sim_queue(u_long arg)
+{
+       struct ahc_softc *ahc;
+       u_long s;
+       int    unblock_reqs;
+
+       ahc = (struct ahc_softc *)arg;
+       unblock_reqs = 0;
+       ahc_lock(ahc, &s);
+       if (ahc->platform_data->qfrozen > 0)
+               ahc->platform_data->qfrozen--;
+       if (ahc->platform_data->qfrozen == 0) {
+               unblock_reqs = 1;
+       }
+       ahc_unlock(ahc, &s);
+       /*
+        * There is still a race here.  The mid-layer
+        * should keep its own freeze count and use
+        * a bottom half handler to run the queues
+        * so we can unblock with our own lock held.
+        */
+       if (unblock_reqs) {
+               scsi_unblock_requests(ahc->platform_data->host);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+               tasklet_schedule(&ahc->platform_data->runq_tasklet);
+               do_softirq();
+#else
+               ahc_runq_tasklet((unsigned long)ahc);
+#endif
+       }
+}
+
+static void
+ahc_linux_dev_timed_unfreeze(u_long arg)
+{
+       struct ahc_linux_device *dev;
+       struct ahc_softc *ahc;
+       u_long s;
+
+       dev = (struct ahc_linux_device *)arg;
+       ahc = dev->target->ahc;
+       ahc_lock(ahc, &s);
+       dev->flags &= ~AHC_DEV_TIMER_ACTIVE;
+       if (dev->qfrozen > 0)
+               dev->qfrozen--;
+       if (dev->qfrozen == 0
+        && (dev->flags & AHC_DEV_ON_RUN_LIST) == 0)
+               ahc_linux_run_device_queue(ahc, dev);
+       ahc_unlock(ahc, &s);
+}
+
+static int
+ahc_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
+{
+       struct ahc_softc *ahc;
+       struct ahc_cmd *acmd;
+       struct ahc_cmd *list_acmd;
+       struct ahc_linux_device *dev;
+       struct scb *pending_scb;
+       u_long s;
+       u_int  saved_scbptr;
+       u_int  active_scb_index;
+       u_int  last_phase;
+       int    retval;
+       int    paused;
+       int    wait;
+       int    disconnected;
+
+       paused = FALSE;
+       wait = FALSE;
+       ahc = *(struct ahc_softc **)cmd->host->hostdata;
+       acmd = (struct ahc_cmd *)cmd;
+
+       printf("%s:%d:%d:%d: Attempting to queue a%s message\n",
+              ahc_name(ahc), cmd->channel, cmd->target, cmd->lun,
+              flag == SCB_ABORT ? "n ABORT" : " TARGET RESET");
+
+       /*
+        * It is a bug that the upper layer takes
+        * this lock just prior to calling us.
+        */
+       spin_unlock_irq(&io_request_lock);
+
+       ahc_lock(ahc, &s);
+
+       /*
+        * First determine if we currently own this command.
+        * Start by searching the device queue.  If not found
+        * there, check the pending_scb list.  If not found
+        * at all, and the system wanted us to just abort the
+        * command return success.
+        */
+       dev = ahc_linux_get_device(ahc, cmd->channel, cmd->target,
+                                  cmd->lun, /*alloc*/FALSE);
+
+       if (dev == NULL) {
+               /*
+                * No target device for this command exists,
+                * so we must not still own the command.
+                */
+               printf("%s:%d:%d:%d: Is not an active device\n",
+                      ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+               retval = SUCCESS;
+               goto no_cmd;
+       }
+
+       TAILQ_FOREACH(list_acmd, &dev->busyq, acmd_links.tqe) {
+               if (list_acmd == acmd)
+                       break;
+       }
+
+       if (list_acmd != NULL) {
+               printf("%s:%d:%d:%d: Command found on device queue\n",
+                      ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+               if (flag == SCB_ABORT) {
+                       TAILQ_REMOVE(&dev->busyq, list_acmd, acmd_links.tqe);
+                       cmd->result = DID_ABORT << 16;
+                       ahc_linux_queue_cmd_complete(ahc, cmd);
+                       retval = SUCCESS;
+                       goto done;
+               }
+       }
+
+       if ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED)) == 0
+        && ahc_search_untagged_queues(ahc, cmd, cmd->target,
+                                      cmd->channel + 'A', cmd->lun,
+                                      CAM_REQ_ABORTED, SEARCH_COMPLETE) != 0) {
+               printf("%s:%d:%d:%d: Command found on untagged queue\n",
+                      ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+               retval = SUCCESS;
+               goto done;
+       }
+
+       /*
+        * See if we can find a matching cmd in the pending list.
+        */
+       LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+               if (pending_scb->io_ctx == cmd)
+                       break;
+       }
+
+       if (pending_scb == NULL && flag == SCB_DEVICE_RESET) {
+
+               /* Any SCB for this device will do for a target reset */
+               LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+                       if (ahc_match_scb(ahc, pending_scb, cmd->target,
+                                         cmd->channel + 'A', CAM_LUN_WILDCARD,
+                                         SCB_LIST_NULL, ROLE_INITIATOR) == 0)
+                               break;
+               }
+       }
+
+       if (pending_scb == NULL) {
+               printf("%s:%d:%d:%d: Command not found\n",
+                      ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+               goto no_cmd;
+       }
+
+       if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) {
+               /*
+                * We can't queue two recovery actions using the same SCB
+                */
+               retval = FAILED;
+               goto  done;
+       }
+
+       /*
+        * Ensure that the card doesn't do anything
+        * behind our back and that no selections have occurred
+        * that have not been serviced.  Also make sure that we
+        * didn't "just" miss an interrupt that would
+        * affect this cmd.
+        */
+       ahc->flags |= AHC_ALL_INTERRUPTS;
+       do {
+               if (paused)
+                       ahc_unpause(ahc);
+               ahc_intr(ahc);
+               ahc_pause(ahc);
+               paused = TRUE;
+               ahc_outb(ahc, SCSISEQ, ahc_inb(ahc, SCSISEQ) & ~ENSELO);
+               ahc_clear_critical_section(ahc);
+       } while ((ahc_inb(ahc, INTSTAT) & INT_PEND) != 0
+             || (ahc_inb(ahc, SSTAT0) & (SELDO|SELINGO)));
+       ahc->flags &= ~AHC_ALL_INTERRUPTS;
+
+       ahc_dump_card_state(ahc);
+
+       if ((pending_scb->flags & SCB_ACTIVE) == 0) {
+               printf("%s:%d:%d:%d: Command already completed\n",
+                      ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+               goto no_cmd;
+       }
+
+       disconnected = TRUE;
+       if (flag == SCB_ABORT) {
+               if (ahc_search_qinfifo(ahc, cmd->target, cmd->channel + 'A',
+                                      cmd->lun, pending_scb->hscb->tag,
+                                      ROLE_INITIATOR, CAM_REQ_ABORTED,
+                                      SEARCH_COMPLETE) > 0) {
+                       printf("%s:%d:%d:%d: Cmd aborted from QINFIFO\n",
+                              ahc_name(ahc), cmd->channel, cmd->target,
+                                       cmd->lun);
+                       retval = SUCCESS;
+                       goto done;
+               }
+       } else if (ahc_search_qinfifo(ahc, cmd->target, cmd->channel + 'A',
+                                     cmd->lun, pending_scb->hscb->tag,
+                                     ROLE_INITIATOR, /*status*/0,
+                                     SEARCH_COUNT) > 0) {
+               disconnected = FALSE;
+       }
+
+       if (disconnected && (ahc_inb(ahc, SEQ_FLAGS) & IDENTIFY_SEEN) != 0) {
+               struct scb *bus_scb;
+
+               bus_scb = ahc_lookup_scb(ahc, ahc_inb(ahc, SCB_TAG));
+               if (bus_scb == pending_scb)
+                       disconnected = FALSE;
+               else if (flag != SCB_ABORT
+                     && ahc_inb(ahc, SAVED_SCSIID) == pending_scb->hscb->scsiid
+                     && ahc_inb(ahc, SAVED_LUN) == pending_scb->hscb->lun)
+                       disconnected = FALSE;
+       }
+
+       /*
+        * At this point, pending_scb is the scb associated with the
+        * passed in command.  That command is currently active on the
+        * bus, is in the disconnected state, or we're hoping to find
+        * a command for the same target active on the bus to abuse to
+        * send a BDR.  Queue the appropriate message based on which of
+        * these states we are in.
+        */
+       last_phase = ahc_inb(ahc, LASTPHASE);
+       saved_scbptr = ahc_inb(ahc, SCBPTR);
+       active_scb_index = ahc_inb(ahc, SCB_TAG);
+       if (last_phase != P_BUSFREE
+        && (pending_scb->hscb->tag == active_scb_index
+         || (flag == SCB_DEVICE_RESET
+          && SCSIID_TARGET(ahc, ahc_inb(ahc, SAVED_SCSIID)) == cmd->target))) {
+
+               /*
+                * We're active on the bus, so assert ATN
+                * and hope that the target responds.
+                */
+               pending_scb = ahc_lookup_scb(ahc, active_scb_index);
+               pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+               ahc_outb(ahc, MSG_OUT, HOST_MSG);
+               ahc_outb(ahc, SCSISIGO, last_phase|ATNO);
+               printf("%s:%d:%d:%d: Device is active, asserting ATN\n",
+                      ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+               wait = TRUE;
+       } else if (disconnected) {
+
+               /*
+                * Actually re-queue this SCB in an attempt
+                * to select the device before it reconnects.
+                * In either case (selection or reselection),
+                * we will now issue the approprate message
+                * to the timed-out device.
+                *
+                * Set the MK_MESSAGE control bit indicating
+                * that we desire to send a message.  We
+                * also set the disconnected flag since
+                * in the paging case there is no guarantee
+                * that our SCB control byte matches the
+                * version on the card.  We don't want the
+                * sequencer to abort the command thinking
+                * an unsolicited reselection occurred.
+                */
+               pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
+               pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+
+               /*
+                * Remove any cached copy of this SCB in the
+                * disconnected list in preparation for the
+                * queuing of our abort SCB.  We use the
+                * same element in the SCB, SCB_NEXT, for
+                * both the qinfifo and the disconnected list.
+                */
+               ahc_search_disc_list(ahc, cmd->target, cmd->channel + 'A',
+                                    cmd->lun, pending_scb->hscb->tag,
+                                    /*stop_on_first*/TRUE,
+                                    /*remove*/TRUE,
+                                    /*save_state*/FALSE);
+
+               /*
+                * In the non-paging case, the sequencer will
+                * never re-reference the in-core SCB.
+                * To make sure we are notified during
+                * reslection, set the MK_MESSAGE flag in
+                * the card's copy of the SCB.
+                */
+               if ((ahc->flags & AHC_PAGESCBS) == 0) {
+                       ahc_outb(ahc, SCBPTR, pending_scb->hscb->tag);
+                       ahc_outb(ahc, SCB_CONTROL,
+                                ahc_inb(ahc, SCB_CONTROL)|MK_MESSAGE);
+               }
+
+               /*
+                * Clear out any entries in the QINFIFO first
+                * so we are the next SCB for this target
+                * to run.
+                */
+               ahc_search_qinfifo(ahc, cmd->target, cmd->channel + 'A',
+                                  cmd->lun, SCB_LIST_NULL, ROLE_INITIATOR,
+                                  CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+               ahc_print_path(ahc, pending_scb);
+               printf("Queuing a recovery SCB\n");
+               ahc_qinfifo_requeue_tail(ahc, pending_scb);
+               ahc_outb(ahc, SCBPTR, saved_scbptr);
+               printf("%s:%d:%d:%d: Device is disconnected, re-queuing SCB\n",
+                      ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+               wait = TRUE;
+       } else {
+               printf("%s:%d:%d:%d: Unable to deliver message\n",
+                      ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+               retval = FAILED;
+               goto done;
+       }
+
+no_cmd:
+       /*
+        * Our assumption is that if we don't have the command, no
+        * recovery action was required, so we return success.  Again,
+        * the semantics of the mid-layer recovery engine are not
+        * well defined, so this may change in time.
+        */
+       retval = SUCCESS;
+done:
+       if (paused)
+               ahc_unpause(ahc);
+       if (wait) {
+         // JWS - XEN  - err...  
+         printf("JWS - aic7xxx: recovery-wait: doh\n");
+         retval=FAILED;
+         /*
+               struct timer_list timer;
+               int ret;
+
+               ahc_unlock(ahc, &s);
+               init_timer(&timer);
+               timer.data = (u_long)&ahc->platform_data->eh_sem;
+               timer.expires = jiffies + (5 * HZ);
+               timer.function = ahc_linux_sem_timeout;
+               add_timer(&timer);
+               printf("Recovery code sleeping\n");
+               down(&ahc->platform_data->eh_sem);
+               printf("Recovery code awake\n");
+               ret = del_timer(&timer);
+               if (ret == 0) {
+                       printf("Timer Expired\n");
+                       retval = FAILED;
+               }
+               ahc_lock(ahc, &s);
+         */
+       }
+       acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+       TAILQ_INIT(&ahc->platform_data->completeq);
+       ahc_unlock(ahc, &s);
+       if (acmd != NULL)
+               ahc_linux_run_complete_queue(ahc, acmd);
+       ahc_runq_tasklet((unsigned long)ahc);
+       spin_lock_irq(&io_request_lock);
+       return (retval);
+}
+
+/*
+ * Abort the current SCSI command(s).
+ */
+int
+ahc_linux_abort(Scsi_Cmnd *cmd)
+{
+       int error;
+
+       error = ahc_linux_queue_recovery_cmd(cmd, SCB_ABORT);
+       if (error != 0)
+               printf("aic7xxx_abort returns 0x%x\n", error);
+       return (error);
+}
+
+/*
+ * Attempt to send a target reset message to the device that timed out.
+ */
+int
+ahc_linux_dev_reset(Scsi_Cmnd *cmd)
+{
+       int error;
+
+       error = ahc_linux_queue_recovery_cmd(cmd, SCB_DEVICE_RESET);
+       if (error != 0)
+               printf("aic7xxx_dev_reset returns 0x%x\n", error);
+       return (error);
+}
+
+/*
+ * Reset the SCSI bus.
+ */
+int
+ahc_linux_bus_reset(Scsi_Cmnd *cmd)
+{
+       struct ahc_softc *ahc;
+       struct ahc_cmd *acmd;
+       u_long s;
+       int    found;
+
+       /*
+        * It is a bug that the upper layer takes
+        * this lock just prior to calling us.
+        */
+       spin_unlock_irq(&io_request_lock);
+
+       ahc = *(struct ahc_softc **)cmd->host->hostdata;
+       ahc_lock(ahc, &s);
+       found = ahc_reset_channel(ahc, cmd->channel + 'A',
+                                 /*initiate reset*/TRUE);
+       acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+       TAILQ_INIT(&ahc->platform_data->completeq);
+       ahc_unlock(ahc, &s);
+       if (bootverbose)
+               printf("%s: SCSI bus reset delivered. "
+                      "%d SCBs aborted.\n", ahc_name(ahc), found);
+
+       if (acmd != NULL)
+               ahc_linux_run_complete_queue(ahc, acmd);
+
+       spin_lock_irq(&io_request_lock);
+       return SUCCESS;
+}
+
+/*
+ * Return the disk geometry for the given SCSI device.
+ */
+int
+ahc_linux_biosparam(Disk *disk, kdev_t dev, int geom[])
+{
+       int     heads;
+       int     sectors;
+       int     cylinders;
+       //int   ret;
+       int     extended;
+       struct  ahc_softc *ahc;
+       //struct        buffer_head *bh;
+
+       ahc = *((struct ahc_softc **)disk->device->host->hostdata);
+#if 0
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
+       bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, block_size(dev));
+#else
+       bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, 1024);
+#endif
+
+       if (bh) {
+               ret = scsi_partsize(bh, disk->capacity,
+                                   &geom[2], &geom[0], &geom[1]);
+               brelse(bh);
+               if (ret != -1)
+                       return (ret);
+       }
+#endif
+       heads = 64;
+       sectors = 32;
+       cylinders = disk->capacity / (heads * sectors);
+
+       if (aic7xxx_extended != 0)
+               extended = 1;
+       else if (disk->device->channel == 0)
+               extended = (ahc->flags & AHC_EXTENDED_TRANS_A) != 0;
+       else
+               extended = (ahc->flags & AHC_EXTENDED_TRANS_B) != 0;
+       if (extended && cylinders >= 1024) {
+               heads = 255;
+               sectors = 63;
+               cylinders = disk->capacity / (heads * sectors);
+       }
+       geom[0] = heads;
+       geom[1] = sectors;
+       geom[2] = cylinders;
+       return (0);
+}
+
+/*
+ * Free the passed in Scsi_Host memory structures prior to unloading the
+ * module.
+ */
+int
+ahc_linux_release(struct Scsi_Host * host)
+{
+       struct ahc_softc *ahc;
+       u_long l;
+
+       ahc_list_lock(&l);
+       if (host != NULL) {
+
+               /*
+                * We should be able to just perform
+                * the free directly, but check our
+                * list for extra sanity.
+                */
+               ahc = ahc_find_softc(*(struct ahc_softc **)host->hostdata);
+               if (ahc != NULL) {
+                       u_long s;
+
+                       ahc_lock(ahc, &s);
+                       ahc_intr_enable(ahc, FALSE);
+                       ahc_unlock(ahc, &s);
+                       ahc_free(ahc);
+               }
+       }
+       ahc_list_unlock(&l);
+       return (0);
+}
+
+void
+ahc_platform_dump_card_state(struct ahc_softc *ahc)
+{
+       struct ahc_linux_device *dev;
+       int channel;
+       int maxchannel;
+       int target;
+       int maxtarget;
+       int lun;
+       int i;
+
+       maxchannel = (ahc->features & AHC_TWIN) ? 1 : 0;
+       maxtarget = (ahc->features & AHC_WIDE) ? 15 : 7;
+       for (channel = 0; channel <= maxchannel; channel++) {
+
+               for (target = 0; target <=maxtarget; target++) {
+
+                       for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+                               struct ahc_cmd *acmd;
+
+                               dev = ahc_linux_get_device(ahc, channel, target,
+                                                          lun, /*alloc*/FALSE);
+                               if (dev == NULL)
+                                       continue;
+
+                               printf("DevQ(%d:%d:%d): ",
+                                      channel, target, lun);
+                               i = 0;
+                               TAILQ_FOREACH(acmd, &dev->busyq,
+                                             acmd_links.tqe) {
+                                       if (i++ > AHC_SCB_MAX)
+                                               break;
+                               }
+                               printf("%d waiting\n", i);
+                       }
+               }
+       }
+}
+
+
+#if defined(MODULE) || LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+static Scsi_Host_Template driver_template = AIC7XXX;
+Scsi_Host_Template *aic7xxx_driver_template = &driver_template;
+#include "../scsi_module.c.inc"
+#endif
+
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_osm.h b/xen/drivers/scsi/aic7xxx/aic7xxx_osm.h
new file mode 100644 (file)
index 0000000..b1a84e7
--- /dev/null
@@ -0,0 +1,1199 @@
+/*
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * Copyright (c) 1994 John Aycock
+ *   The University of Calgary Department of Computer Science.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.h#82 $
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm.h#82 $
+ *
+ */
+
+#ifndef _AIC7XXX_LINUX_H_
+#define _AIC7XXX_LINUX_H_
+#include <xeno/lib.h>
+#include <xeno/string.h>
+#include <xeno/types.h>
+#include <xeno/blk.h>
+#include <xeno/blkdev.h>
+#include <xeno/delay.h>
+#include <xeno/ioport.h>
+#include <xeno/pci.h>
+//#include <xeno/version.h>
+#ifndef AHC_MODVERSION_FILE
+#define __NO_VERSION__
+#endif
+#include <xeno/module.h>
+#include <asm/byteorder.h>
+#include <xeno/notifier.h>
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
+#endif
+
+#define LINUX_VERSION_CODE KERNEL_VERSION(2,4,20)
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#include <xeno/interrupt.h> /* For tasklet support. */
+#include <xeno/config.h>
+#include <xeno/slab.h>
+#else
+#include <xeno/malloc.h>
+#endif
+
+/* Core SCSI definitions */
+#include "../scsi.h"
+#include "../hosts.h"
+
+/* Name space conflict with BSD queue macros */
+#ifdef LIST_HEAD
+#undef LIST_HEAD
+#endif
+
+#include "cam.h"
+#include "queue.h"
+#include "scsi_message.h"
+
+/************************* Forward Declarations *******************************/
+struct ahc_softc;
+typedef struct pci_dev *ahc_dev_softc_t;
+typedef Scsi_Cmnd      *ahc_io_ctx_t;
+
+/******************************* Byte Order ***********************************/
+#define ahc_htobe16(x) cpu_to_be16(x)
+#define ahc_htobe32(x) cpu_to_be32(x)
+#define ahc_htobe64(x) cpu_to_be64(x)
+#define ahc_htole16(x) cpu_to_le16(x)
+#define ahc_htole32(x) cpu_to_le32(x)
+#define ahc_htole64(x) cpu_to_le64(x)
+
+#define ahc_be16toh(x) be16_to_cpu(x)
+#define ahc_be32toh(x) be32_to_cpu(x)
+#define ahc_be64toh(x) be64_to_cpu(x)
+#define ahc_le16toh(x) le16_to_cpu(x)
+#define ahc_le32toh(x) le32_to_cpu(x)
+#define ahc_le64toh(x) le64_to_cpu(x)
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+#ifndef BYTE_ORDER
+#if defined(__BIG_ENDIAN)
+#define BYTE_ORDER BIG_ENDIAN
+#endif
+#if defined(__LITTLE_ENDIAN)
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#endif /* BYTE_ORDER */
+
+/************************* Configuration Data *********************************/
+extern int aic7xxx_no_probe;
+extern int aic7xxx_detect_complete;
+extern Scsi_Host_Template* aic7xxx_driver_template;
+
+/***************************** Bus Space/DMA **********************************/
+
+//#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,17)
+typedef dma_addr_t bus_addr_t;
+//#else
+//typedef uint32_t bus_addr_t;
+//#endif
+typedef uint32_t bus_size_t;
+
+typedef enum {
+       BUS_SPACE_MEMIO,
+       BUS_SPACE_PIO
+} bus_space_tag_t;
+
+typedef union {
+       u_long            ioport;
+       volatile uint8_t *maddr;
+} bus_space_handle_t;
+
+typedef struct bus_dma_segment
+{
+       bus_addr_t      ds_addr;
+       bus_size_t      ds_len;
+} bus_dma_segment_t;
+
+struct ahc_linux_dma_tag
+{
+       bus_size_t      alignment;
+       bus_size_t      boundary;
+       bus_size_t      maxsize;
+};
+typedef struct ahc_linux_dma_tag* bus_dma_tag_t;
+
+struct ahc_linux_dmamap
+{
+       bus_addr_t      bus_addr;
+};
+typedef struct ahc_linux_dmamap* bus_dmamap_t;
+
+typedef int bus_dma_filter_t(void*, bus_addr_t);
+typedef void bus_dmamap_callback_t(void *, bus_dma_segment_t *, int, int);
+
+#define BUS_DMA_WAITOK         0x0
+#define BUS_DMA_NOWAIT         0x1
+#define BUS_DMA_ALLOCNOW       0x2
+#define BUS_DMA_LOAD_SEGS      0x4     /*
+                                        * Argument is an S/G list not
+                                        * a single buffer.
+                                        */
+
+#define BUS_SPACE_MAXADDR      0xFFFFFFFF
+#define BUS_SPACE_MAXADDR_32BIT        0xFFFFFFFF
+#define BUS_SPACE_MAXSIZE_32BIT        0xFFFFFFFF
+
+int    ahc_dma_tag_create(struct ahc_softc *, bus_dma_tag_t /*parent*/,
+                          bus_size_t /*alignment*/, bus_size_t /*boundary*/,
+                          bus_addr_t /*lowaddr*/, bus_addr_t /*highaddr*/,
+                          bus_dma_filter_t*/*filter*/, void */*filterarg*/,
+                          bus_size_t /*maxsize*/, int /*nsegments*/,
+                          bus_size_t /*maxsegsz*/, int /*flags*/,
+                          bus_dma_tag_t */*dma_tagp*/);
+
+void   ahc_dma_tag_destroy(struct ahc_softc *, bus_dma_tag_t /*tag*/);
+
+int    ahc_dmamem_alloc(struct ahc_softc *, bus_dma_tag_t /*dmat*/,
+                        void** /*vaddr*/, int /*flags*/,
+                        bus_dmamap_t* /*mapp*/);
+
+void   ahc_dmamem_free(struct ahc_softc *, bus_dma_tag_t /*dmat*/,
+                       void* /*vaddr*/, bus_dmamap_t /*map*/);
+
+void   ahc_dmamap_destroy(struct ahc_softc *, bus_dma_tag_t /*tag*/,
+                          bus_dmamap_t /*map*/);
+
+int    ahc_dmamap_load(struct ahc_softc *ahc, bus_dma_tag_t /*dmat*/,
+                       bus_dmamap_t /*map*/, void * /*buf*/,
+                       bus_size_t /*buflen*/, bus_dmamap_callback_t *,
+                       void */*callback_arg*/, int /*flags*/);
+
+int    ahc_dmamap_unload(struct ahc_softc *, bus_dma_tag_t, bus_dmamap_t);
+
+/*
+ * Operations performed by ahc_dmamap_sync().
+ */
+#define BUS_DMASYNC_PREREAD    0x01    /* pre-read synchronization */
+#define BUS_DMASYNC_POSTREAD   0x02    /* post-read synchronization */
+#define BUS_DMASYNC_PREWRITE   0x04    /* pre-write synchronization */
+#define BUS_DMASYNC_POSTWRITE  0x08    /* post-write synchronization */
+
+/*
+ * XXX
+ * ahc_dmamap_sync is only used on buffers allocated with
+ * the pci_alloc_consistent() API.  Although I'm not sure how
+ * this works on architectures with a write buffer, Linux does
+ * not have an API to sync "coherent" memory.  Perhaps we need
+ * to do an mb()?
+ */
+#define ahc_dmamap_sync(ahc, dma_tag, dmamap, offset, len, op)
+
+/************************** SCSI Constants/Structures *************************/
+#define SCSI_REV_2 2
+#define        SCSI_STATUS_OK                  0x00
+#define        SCSI_STATUS_CHECK_COND          0x02
+#define        SCSI_STATUS_COND_MET            0x04
+#define        SCSI_STATUS_BUSY                0x08
+#define SCSI_STATUS_INTERMED           0x10
+#define SCSI_STATUS_INTERMED_COND_MET  0x14
+#define SCSI_STATUS_RESERV_CONFLICT    0x18
+#define SCSI_STATUS_CMD_TERMINATED     0x22
+#define SCSI_STATUS_QUEUE_FULL         0x28
+
+/*
+ * 6 byte request sense CDB format.
+ */
+struct scsi_sense
+{
+       uint8_t opcode;
+       uint8_t byte2;
+       uint8_t unused[2];
+       uint8_t length;
+       uint8_t control;
+};
+
+struct scsi_sense_data
+{
+       uint8_t error_code;
+       uint8_t segment;
+       uint8_t flags;
+       uint8_t info[4];
+       uint8_t extra_len;
+       uint8_t cmd_spec_info[4];
+       uint8_t add_sense_code;
+       uint8_t add_sense_code_qual;
+       uint8_t fru;
+       uint8_t sense_key_spec[3];
+       uint8_t extra_bytes[14];
+};
+
+struct scsi_inquiry
+{ 
+       u_int8_t opcode;
+       u_int8_t byte2;
+#define        SI_EVPD 0x01
+       u_int8_t page_code;
+       u_int8_t reserved;
+       u_int8_t length;
+       u_int8_t control;
+};
+
+struct scsi_inquiry_data
+{
+       uint8_t device;
+#define        SID_TYPE(inq_data) ((inq_data)->device & 0x1f)
+#define        SID_QUAL(inq_data) (((inq_data)->device & 0xE0) >> 5)
+#define        SID_QUAL_LU_CONNECTED   0x00    /*
+                                        * The specified peripheral device
+                                        * type is currently connected to
+                                        * logical unit.  If the target cannot
+                                        * determine whether or not a physical
+                                        * device is currently connected, it
+                                        * shall also use this peripheral
+                                        * qualifier when returning the INQUIRY
+                                        * data.  This peripheral qualifier
+                                        * does not mean that the device is
+                                        * ready for access by the initiator.
+                                        */
+#define        SID_QUAL_LU_OFFLINE     0x01    /*
+                                        * The target is capable of supporting
+                                        * the specified peripheral device type
+                                        * on this logical unit; however, the
+                                        * physical device is not currently
+                                        * connected to this logical unit.
+                                        */
+#define SID_QUAL_RSVD          0x02
+#define        SID_QUAL_BAD_LU         0x03    /*
+                                        * The target is not capable of
+                                        * supporting a physical device on
+                                        * this logical unit. For this
+                                        * peripheral qualifier the peripheral
+                                        * device type shall be set to 1Fh to
+                                        * provide compatibility with previous
+                                        * versions of SCSI. All other
+                                        * peripheral device type values are
+                                        * reserved for this peripheral
+                                        * qualifier.
+                                        */
+#define        SID_QUAL_IS_VENDOR_UNIQUE(inq_data) ((SID_QUAL(inq_data) & 0x08) != 0)
+       uint8_t dev_qual2;
+#define        SID_QUAL2       0x7F
+#define        SID_IS_REMOVABLE(inq_data) (((inq_data)->dev_qual2 & 0x80) != 0)
+       uint8_t version;
+#define SID_ANSI_REV(inq_data) ((inq_data)->version & 0x07)
+#define                SCSI_REV_0              0
+#define                SCSI_REV_CCS            1
+#define                SCSI_REV_2              2
+#define                SCSI_REV_SPC            3
+#define                SCSI_REV_SPC2           4
+
+#define SID_ECMA       0x38
+#define SID_ISO                0xC0
+       uint8_t response_format;
+#define SID_AENC       0x80
+#define SID_TrmIOP     0x40
+       uint8_t additional_length;
+       uint8_t reserved[2];
+       uint8_t flags;
+#define        SID_SftRe       0x01
+#define        SID_CmdQue      0x02
+#define        SID_Linked      0x08
+#define        SID_Sync        0x10
+#define        SID_WBus16      0x20
+#define        SID_WBus32      0x40
+#define        SID_RelAdr      0x80
+#define SID_VENDOR_SIZE   8
+       char     vendor[SID_VENDOR_SIZE];
+#define SID_PRODUCT_SIZE  16
+       char     product[SID_PRODUCT_SIZE];
+#define SID_REVISION_SIZE 4
+       char     revision[SID_REVISION_SIZE];
+       /*
+        * The following fields were taken from SCSI Primary Commands - 2
+        * (SPC-2) Revision 14, Dated 11 November 1999
+        */
+#define        SID_VENDOR_SPECIFIC_0_SIZE      20
+       u_int8_t vendor_specific0[SID_VENDOR_SPECIFIC_0_SIZE];
+       /*
+        * An extension of SCSI Parallel Specific Values
+        */
+#define        SID_SPI_IUS             0x01
+#define        SID_SPI_QAS             0x02
+#define        SID_SPI_CLOCK_ST        0x00
+#define        SID_SPI_CLOCK_DT        0x04
+#define        SID_SPI_CLOCK_DT_ST     0x0C
+#define        SID_SPI_MASK            0x0F
+       uint8_t spi3data;
+       uint8_t reserved2;
+       /*
+        * Version Descriptors, stored 2 byte values.
+        */
+       uint8_t version1[2];
+       uint8_t version2[2];
+       uint8_t version3[2];
+       uint8_t version4[2];
+       uint8_t version5[2];
+       uint8_t version6[2];
+       uint8_t version7[2];
+       uint8_t version8[2];
+
+       uint8_t reserved3[22];
+
+#define        SID_VENDOR_SPECIFIC_1_SIZE      160
+       uint8_t vendor_specific1[SID_VENDOR_SPECIFIC_1_SIZE];
+};
+
+/********************************** Includes **********************************/
+/* Host template and function declarations referenced by the template. */
+#include "aic7xxx_host.h"
+
+/* Core driver definitions */
+#include "aic7xxx.h"
+
+/* SMP support */
+//#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,17)
+#include <xeno/spinlock.h>
+//#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93)
+//#include <xeno/smp.h>
+//#endif
+
+#define AIC7XXX_DRIVER_VERSION  "6.2.8"
+
+/**************************** Front End Queues ********************************/
+/*
+ * Data structure used to cast the Linux struct scsi_cmnd to something
+ * that allows us to use the queue macros.  The linux structure has
+ * plenty of space to hold the links fields as required by the queue
+ * macros, but the queue macors require them to have the correct type.
+ */
+struct ahc_cmd_internal {
+       /* Area owned by the Linux scsi layer. */
+       uint8_t private[offsetof(struct scsi_cmnd, SCp.Status)];
+       union {
+               STAILQ_ENTRY(ahc_cmd)   ste;
+               LIST_ENTRY(ahc_cmd)     le;
+               TAILQ_ENTRY(ahc_cmd)    tqe;
+       } links;
+       uint32_t                        end;
+};
+
+struct ahc_cmd {
+       union {
+               struct ahc_cmd_internal icmd;
+               struct scsi_cmnd        scsi_cmd;
+       } un;
+};
+
+#define acmd_icmd(cmd) ((cmd)->un.icmd)
+#define acmd_scsi_cmd(cmd) ((cmd)->un.scsi_cmd)
+#define acmd_links un.icmd.links
+
+/*************************** Device Data Structures ***************************/
+/*
+ * A per probed device structure used to deal with some error recovery
+ * scenarios that the Linux mid-layer code just doesn't know how to
+ * handle.  The structure allocated for a device only becomes persistant
+ * after a successfully completed inquiry command to the target when
+ * that inquiry data indicates a lun is present.
+ */
+TAILQ_HEAD(ahc_busyq, ahc_cmd);
+typedef enum {
+       AHC_DEV_UNCONFIGURED     = 0x01,
+       AHC_DEV_FREEZE_TIL_EMPTY = 0x02, /* Freeze queue until active == 0 */
+       AHC_DEV_TIMER_ACTIVE     = 0x04, /* Our timer is active */
+       AHC_DEV_ON_RUN_LIST      = 0x08, /* Queued to be run later */
+       AHC_DEV_Q_BASIC          = 0x10, /* Allow basic device queuing */
+       AHC_DEV_Q_TAGGED         = 0x20, /* Allow full SCSI2 command queueing */
+       AHC_DEV_PERIODIC_OTAG    = 0x40  /* Send OTAG to prevent starvation */
+} ahc_dev_flags;
+
+struct ahc_linux_target;
+struct ahc_linux_device {
+       TAILQ_ENTRY(ahc_linux_device) links;
+       struct          ahc_busyq busyq;
+
+       /*
+        * The number of transactions currently
+        * queued to the device.
+        */
+       int                     active;
+
+       /*
+        * The currently allowed number of 
+        * transactions that can be queued to
+        * the device.  Must be signed for
+        * conversion from tagged to untagged
+        * mode where the device may have more
+        * than one outstanding active transaction.
+        */
+       int                     openings;
+
+       /*
+        * A positive count indicates that this
+        * device's queue is halted.
+        */
+       u_int                   qfrozen;
+       
+       /*
+        * Cumulative command counter.
+        */
+       u_long                  commands_issued;
+
+       /*
+        * The number of tagged transactions when
+        * running at our current opening level
+        * that have been successfully received by
+        * this device since the last QUEUE FULL.
+        */
+       u_int                   tag_success_count;
+#define AHC_TAG_SUCCESS_INTERVAL 50
+
+       ahc_dev_flags           flags;
+
+       /*
+        * Per device timer.
+        */
+       struct timer_list       timer;
+
+       /*
+        * The high limit for the tags variable.
+        */
+       u_int                   maxtags;
+
+       /*
+        * The computed number of tags outstanding
+        * at the time of the last QUEUE FULL event.
+        */
+       u_int                   tags_on_last_queuefull;
+
+       /*
+        * How many times we have seen a queue full
+        * with the same number of tags.  This is used
+        * to stop our adaptive queue depth algorithm
+        * on devices with a fixed number of tags.
+        */
+       u_int                   last_queuefull_same_count;
+#define AHC_LOCK_TAGS_COUNT 50
+
+       /*
+        * How many transactions have been queued
+        * without the device going idle.  We use
+        * this statistic to determine when to issue
+        * an ordered tag to prevent transaction
+        * starvation.  This statistic is only updated
+        * if the AHC_DEV_PERIODIC_OTAG flag is set
+        * on this device.
+        */
+       u_int                   commands_since_idle_or_otag;
+#define AHC_OTAG_THRESH        500
+
+       int                     lun;
+       struct                  ahc_linux_target *target;
+};
+
+struct ahc_linux_target {
+       struct  ahc_linux_device *devices[AHC_NUM_LUNS];
+       int     channel;
+       int     target;
+       int     refcount;
+       struct  ahc_transinfo last_tinfo;
+       struct  ahc_softc *ahc;
+};
+
+/********************* Definitions Required by the Core ***********************/
+/*
+ * Number of SG segments we require.  So long as the S/G segments for
+ * a particular transaction are allocated in a physically contiguous
+ * manner and are allocated below 4GB, the number of S/G segments is
+ * unrestricted.
+ */
+#define        AHC_NSEG 128
+
+/*
+ * Per-SCB OSM storage.
+ */
+struct scb_platform_data {
+       struct ahc_linux_device *dev;
+       bus_addr_t               buf_busaddr;
+       uint32_t                 xfer_len;
+  //#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+       uint32_t                 resid;         /* Transfer residual */
+  //#endif
+};
+
+/*
+ * Define a structure used for each host adapter.  All members are
+ * aligned on a boundary >= the size of the member to honor the
+ * alignment restrictions of the various platforms supported by
+ * this driver.
+ */
+TAILQ_HEAD(ahc_completeq, ahc_cmd);
+struct ahc_platform_data {
+       /*
+        * Fields accessed from interrupt context.
+        */
+       struct ahc_linux_target *targets[AHC_NUM_TARGETS]; 
+       TAILQ_HEAD(, ahc_linux_device) device_runq;
+       struct ahc_completeq     completeq;
+
+  //#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+       spinlock_t               spin_lock;
+  //#endif
+  //#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+       struct tasklet_struct    runq_tasklet;
+  //#endif
+       u_int                    qfrozen;
+       struct timer_list        reset_timer;
+  //   struct semaphore         eh_sem;
+       struct Scsi_Host        *host;          /* pointer to scsi host */
+#define AHC_LINUX_NOIRQ        ((uint32_t)~0)
+       uint32_t                 irq;           /* IRQ for this adapter */
+       uint32_t                 bios_address;
+       uint32_t                 mem_busaddr;   /* Mem Base Addr */
+       bus_addr_t               hw_dma_mask;
+};
+
+/************************** OS Utility Wrappers *******************************/
+#define printf printk
+#define M_NOWAIT GFP_ATOMIC
+#define M_WAITOK 0
+#define malloc(size, type, flags) kmalloc(size, flags)
+#define free(ptr, type) kfree(ptr)
+
+static __inline void ahc_delay(long);
+static __inline void
+ahc_delay(long usec)
+{
+       /*
+        * udelay on Linux can have problems for
+        * multi-millisecond waits.  Wait at most
+        * 1024us per call.
+        */
+       while (usec > 0) {
+               udelay(usec % 1024);
+               usec -= 1024;
+       }
+}
+
+
+/***************************** Low Level I/O **********************************/
+#if defined(__powerpc__) || defined(__i386__) || defined(__ia64__)
+#define MMAPIO
+#endif
+
+static __inline uint8_t ahc_inb(struct ahc_softc * ahc, long port);
+static __inline void ahc_outb(struct ahc_softc * ahc, long port, uint8_t val);
+static __inline void ahc_outsb(struct ahc_softc * ahc, long port,
+                              uint8_t *, int count);
+static __inline void ahc_insb(struct ahc_softc * ahc, long port,
+                              uint8_t *, int count);
+
+static __inline uint8_t
+ahc_inb(struct ahc_softc * ahc, long port)
+{
+       uint8_t x;
+#ifdef MMAPIO
+
+       if (ahc->tag == BUS_SPACE_MEMIO) {
+               x = readb(ahc->bsh.maddr + port);
+       } else {
+               x = inb(ahc->bsh.ioport + port);
+       }
+#else
+       x = inb(ahc->bsh.ioport + port);
+#endif
+       mb();
+       return (x);
+}
+
+static __inline void
+ahc_outb(struct ahc_softc * ahc, long port, uint8_t val)
+{
+#ifdef MMAPIO
+       if (ahc->tag == BUS_SPACE_MEMIO) {
+               writeb(val, ahc->bsh.maddr + port);
+       } else {
+               outb(val, ahc->bsh.ioport + port);
+       }
+#else
+       outb(val, ahc->bsh.ioport + port);
+#endif
+       mb();
+}
+
+static __inline void
+ahc_outsb(struct ahc_softc * ahc, long port, uint8_t *array, int count)
+{
+       int i;
+
+       /*
+        * There is probably a more efficient way to do this on Linux
+        * but we don't use this for anything speed critical and this
+        * should work.
+        */
+       for (i = 0; i < count; i++)
+               ahc_outb(ahc, port, *array++);
+}
+
+static __inline void
+ahc_insb(struct ahc_softc * ahc, long port, uint8_t *array, int count)
+{
+       int i;
+
+       /*
+        * There is probably a more efficient way to do this on Linux
+        * but we don't use this for anything speed critical and this
+        * should work.
+        */
+       for (i = 0; i < count; i++)
+               *array++ = ahc_inb(ahc, port);
+}
+
+/**************************** Initialization **********************************/
+int            ahc_linux_register_host(struct ahc_softc *,
+                                       Scsi_Host_Template *);
+
+uint64_t       ahc_linux_get_memsize(void);
+
+/*************************** Pretty Printing **********************************/
+
+#define off_t unsigned long
+
+struct info_str {
+       char *buffer;
+       int length;
+       off_t offset;
+       int pos;
+};
+
+void   ahc_format_transinfo(struct info_str *info,
+                            struct ahc_transinfo *tinfo);
+
+
+/******************************** Locking *************************************/
+/* Lock protecting internal data structures */
+static __inline void ahc_lockinit(struct ahc_softc *);
+static __inline void ahc_lock(struct ahc_softc *, unsigned long *flags);
+static __inline void ahc_unlock(struct ahc_softc *, unsigned long *flags);
+
+/* Lock held during command compeletion to the upper layer */
+static __inline void ahc_done_lockinit(struct ahc_softc *);
+static __inline void ahc_done_lock(struct ahc_softc *, unsigned long *flags);
+static __inline void ahc_done_unlock(struct ahc_softc *, unsigned long *flags);
+
+/* Lock held during ahc_list manipulation and ahc softc frees */
+extern spinlock_t ahc_list_spinlock;
+static __inline void ahc_list_lockinit(void);
+static __inline void ahc_list_lock(unsigned long *flags);
+static __inline void ahc_list_unlock(unsigned long *flags);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93)
+static __inline void
+ahc_lockinit(struct ahc_softc *ahc)
+{
+       spin_lock_init(&ahc->platform_data->spin_lock);
+}
+
+static __inline void
+ahc_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+       *flags = 0;
+       spin_lock_irqsave(&ahc->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahc_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+       spin_unlock_irqrestore(&ahc->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahc_done_lockinit(struct ahc_softc *ahc)
+{
+       /* We don't own the iorequest lock, so we don't initialize it. */
+}
+
+static __inline void
+ahc_done_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+       *flags = 0;
+       spin_lock_irqsave(&io_request_lock, *flags);
+}
+
+static __inline void
+ahc_done_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+       spin_unlock_irqrestore(&io_request_lock, *flags);
+}
+
+static __inline void
+ahc_list_lockinit()
+{
+       spin_lock_init(&ahc_list_spinlock);
+}
+
+static __inline void
+ahc_list_lock(unsigned long *flags)
+{
+       *flags = 0;
+       spin_lock_irqsave(&ahc_list_spinlock, *flags);
+}
+
+static __inline void
+ahc_list_unlock(unsigned long *flags)
+{
+       spin_unlock_irqrestore(&ahc_list_spinlock, *flags);
+}
+
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) */
+
+ahc_lockinit(struct ahc_softc *ahc)
+{
+}
+
+static __inline void
+ahc_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+       *flags = 0;
+       save_flags(*flags);
+       cli();
+}
+
+static __inline void
+ahc_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+       restore_flags(*flags);
+}
+
+static __inline void
+ahc_done_lockinit(struct ahc_softc *ahc)
+{
+}
+
+static __inline void
+ahc_done_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+       /*
+        * The done lock is always held while
+        * the ahc lock is held so blocking
+        * interrupts again would have no effect.
+        */
+}
+
+static __inline void
+ahc_done_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+}
+
+static __inline void
+ahc_list_lockinit()
+{
+}
+
+static __inline void
+ahc_list_lock(unsigned long *flags)
+{
+       *flags = 0;
+       save_flags(*flags);
+       cli();
+}
+
+static __inline void
+ahc_list_unlock(unsigned long *flags)
+{
+       restore_flags(*flags);
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) */
+
+/******************************* PCI Definitions ******************************/
+/*
+ * PCIM_xxx: mask to locate subfield in register
+ * PCIR_xxx: config register offset
+ * PCIC_xxx: device class
+ * PCIS_xxx: device subclass
+ * PCIP_xxx: device programming interface
+ * PCIV_xxx: PCI vendor ID (only required to fixup ancient devices)
+ * PCID_xxx: device ID
+ */
+#define PCIR_DEVVENDOR         0x00
+#define PCIR_VENDOR            0x00
+#define PCIR_DEVICE            0x02
+#define PCIR_COMMAND           0x04
+#define PCIM_CMD_PORTEN                0x0001
+#define PCIM_CMD_MEMEN         0x0002
+#define PCIM_CMD_BUSMASTEREN   0x0004
+#define PCIM_CMD_MWRICEN       0x0010
+#define PCIM_CMD_PERRESPEN     0x0040
+#define PCIR_STATUS            0x06
+#define PCIR_REVID             0x08
+#define PCIR_PROGIF            0x09
+#define PCIR_SUBCLASS          0x0a
+#define PCIR_CLASS             0x0b
+#define PCIR_CACHELNSZ         0x0c
+#define PCIR_LATTIMER          0x0d
+#define PCIR_HEADERTYPE                0x0e
+#define PCIM_MFDEV             0x80
+#define PCIR_BIST              0x0f
+#define PCIR_CAP_PTR           0x34
+
+/* config registers for header type 0 devices */
+#define PCIR_MAPS      0x10
+#define PCIR_SUBVEND_0 0x2c
+#define PCIR_SUBDEV_0  0x2e
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+extern struct pci_driver aic7xxx_pci_driver;
+#endif
+
+typedef enum
+{
+       AHC_POWER_STATE_D0,
+       AHC_POWER_STATE_D1,
+       AHC_POWER_STATE_D2,
+       AHC_POWER_STATE_D3
+} ahc_power_state;
+
+void ahc_power_state_change(struct ahc_softc *ahc,
+                           ahc_power_state new_state);
+/**************************** VL/EISA Routines ********************************/
+int                     aic7770_linux_probe(Scsi_Host_Template *);
+int                     aic7770_map_registers(struct ahc_softc *ahc,
+                                              u_int port);
+int                     aic7770_map_int(struct ahc_softc *ahc, u_int irq);
+
+/******************************* PCI Routines *********************************/
+/*
+ * We need to use the bios32.h routines if we are kernel version 2.1.92 or less.
+ */
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,1,92)
+#if defined(__sparc_v9__) || defined(__powerpc__)
+#error "PPC and Sparc platforms are only support under 2.1.92 and above"
+#endif
+#include <xeno/bios32.h>
+#endif
+
+int                     ahc_linux_pci_probe(Scsi_Host_Template *);
+int                     ahc_pci_map_registers(struct ahc_softc *ahc);
+int                     ahc_pci_map_int(struct ahc_softc *ahc);
+
+static __inline uint32_t ahc_pci_read_config(ahc_dev_softc_t pci,
+                                            int reg, int width);
+
+static __inline uint32_t
+ahc_pci_read_config(ahc_dev_softc_t pci, int reg, int width)
+{
+       switch (width) {
+       case 1:
+       {
+               uint8_t retval;
+
+               pci_read_config_byte(pci, reg, &retval);
+               return (retval);
+       }
+       case 2:
+       {
+               uint16_t retval;
+               pci_read_config_word(pci, reg, &retval);
+               return (retval);
+       }
+       case 4:
+       {
+               uint32_t retval;
+               pci_read_config_dword(pci, reg, &retval);
+               return (retval);
+       }
+       default:
+               panic("ahc_pci_read_config: Read size too big");
+               /* NOTREACHED */
+               return (0);
+       }
+}
+
+static __inline void ahc_pci_write_config(ahc_dev_softc_t pci,
+                                         int reg, uint32_t value,
+                                         int width);
+
+static __inline void
+ahc_pci_write_config(ahc_dev_softc_t pci, int reg, uint32_t value, int width)
+{
+       switch (width) {
+       case 1:
+               pci_write_config_byte(pci, reg, value);
+               break;
+       case 2:
+               pci_write_config_word(pci, reg, value);
+               break;
+       case 4:
+               pci_write_config_dword(pci, reg, value);
+               break;
+       default:
+               panic("ahc_pci_write_config: Write size too big");
+               /* NOTREACHED */
+       }
+}
+
+static __inline int ahc_get_pci_function(ahc_dev_softc_t);
+static __inline int
+ahc_get_pci_function(ahc_dev_softc_t pci)
+{
+       return (PCI_FUNC(pci->devfn));
+}
+
+static __inline int ahc_get_pci_slot(ahc_dev_softc_t);
+static __inline int
+ahc_get_pci_slot(ahc_dev_softc_t pci)
+{
+       return (PCI_SLOT(pci->devfn));
+}
+
+static __inline int ahc_get_pci_bus(ahc_dev_softc_t);
+static __inline int
+ahc_get_pci_bus(ahc_dev_softc_t pci)
+{
+       return (pci->bus->number);
+}
+
+static __inline void ahc_flush_device_writes(struct ahc_softc *);
+static __inline void
+ahc_flush_device_writes(struct ahc_softc *ahc)
+{
+       /* XXX Is this sufficient for all architectures??? */
+       ahc_inb(ahc, INTSTAT);
+}
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,0)
+#define pci_map_sg(pdev, sg_list, nseg, direction) (nseg)
+#define pci_unmap_sg(pdev, sg_list, nseg, direction)
+#define sg_dma_address(sg) (VIRT_TO_BUS((sg)->address))
+#define sg_dma_len(sg) ((sg)->length)
+#define pci_map_single(pdev, buffer, bufflen, direction) \
+       (VIRT_TO_BUS(buffer))
+#define pci_unmap_single(pdev, buffer, buflen, direction)
+#endif
+
+/*********************** Transaction Access Wrappers **************************/
+static __inline void ahc_set_transaction_status(struct scb *, uint32_t);
+static __inline
+void ahc_set_transaction_status(struct scb *scb, uint32_t status)
+{
+       scb->io_ctx->result &= ~(CAM_STATUS_MASK << 16);
+       scb->io_ctx->result |= status << 16;
+}
+
+static __inline void ahc_set_scsi_status(struct scb *, uint32_t);
+static __inline
+void ahc_set_scsi_status(struct scb *scb, uint32_t status)
+{
+       scb->io_ctx->result &= ~0xFFFF;
+       scb->io_ctx->result |= status;
+}
+
+static __inline uint32_t ahc_get_transaction_status(struct scb *);
+static __inline
+uint32_t ahc_get_transaction_status(struct scb *scb)
+{
+       return ((scb->io_ctx->result >> 16) & CAM_STATUS_MASK);
+}
+
+static __inline uint32_t ahc_get_scsi_status(struct scb *);
+static __inline
+uint32_t ahc_get_scsi_status(struct scb *scb)
+{
+       return (scb->io_ctx->result & 0xFFFF);
+}
+
+static __inline void ahc_set_transaction_tag(struct scb *, int, u_int);
+static __inline
+void ahc_set_transaction_tag(struct scb *scb, int enabled, u_int type)
+{
+       /*
+        * Nothing to do for linux as the incoming transaction
+        * has no concept of tag/non tagged, etc.
+        */
+}
+
+static __inline u_long ahc_get_transfer_length(struct scb *);
+static __inline
+u_long ahc_get_transfer_length(struct scb *scb)
+{
+       return (scb->platform_data->xfer_len);
+}
+
+static __inline int ahc_get_transfer_dir(struct scb *);
+static __inline
+int ahc_get_transfer_dir(struct scb *scb)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,40)
+       return (scb->io_ctx->sc_data_direction);
+#else
+       if (scb->io_ctx->bufflen == 0)
+               return (CAM_DIR_NONE);
+
+       switch(scb->io_ctx->cmnd[0]) {
+       case 0x08:  /* READ(6)  */
+       case 0x28:  /* READ(10) */
+       case 0xA8:  /* READ(12) */
+               return (CAM_DIR_IN);
+        case 0x0A:  /* WRITE(6)  */
+        case 0x2A:  /* WRITE(10) */
+        case 0xAA:  /* WRITE(12) */
+               return (CAM_DIR_OUT);
+        default:
+               return (CAM_DIR_NONE);
+        }
+#endif
+}
+
+static __inline void ahc_set_residual(struct scb *, u_long);
+static __inline
+void ahc_set_residual(struct scb *scb, u_long resid)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       scb->io_ctx->resid = resid;
+#else
+       scb->platform_data->resid = resid;
+#endif
+}
+
+static __inline void ahc_set_sense_residual(struct scb *, u_long);
+static __inline
+void ahc_set_sense_residual(struct scb *scb, u_long resid)
+{
+       /* This can't be reported in Linux */
+}
+
+static __inline u_long ahc_get_residual(struct scb *);
+static __inline
+u_long ahc_get_residual(struct scb *scb)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       return (scb->io_ctx->resid);
+#else
+       return (scb->platform_data->resid);
+#endif
+}
+
+static __inline int ahc_perform_autosense(struct scb *);
+static __inline
+int ahc_perform_autosense(struct scb *scb)
+{
+       /*
+        * We always perform autosense in Linux.
+        * On other platforms this is set on a
+        * per-transaction basis.
+        */
+       return (1);
+}
+
+static __inline uint32_t
+ahc_get_sense_bufsize(struct ahc_softc *ahc, struct scb *scb)
+{
+       return (sizeof(struct scsi_sense_data));
+}
+
+static __inline void ahc_notify_xfer_settings_change(struct ahc_softc *,
+                                                    struct ahc_devinfo *);
+static __inline void
+ahc_notify_xfer_settings_change(struct ahc_softc *ahc,
+                               struct ahc_devinfo *devinfo)
+{
+       /* Nothing to do here for linux */
+}
+
+static __inline void ahc_platform_scb_free(struct ahc_softc *ahc,
+                                          struct scb *scb);
+static __inline void
+ahc_platform_scb_free(struct ahc_softc *ahc, struct scb *scb)
+{
+       ahc->flags &= ~AHC_RESOURCE_SHORTAGE;
+}
+
+int    ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg);
+void   ahc_platform_free(struct ahc_softc *ahc);
+void   ahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb);
+static __inline void   ahc_freeze_scb(struct scb *scb);
+static __inline void
+ahc_freeze_scb(struct scb *scb)
+{
+       /* Noting to do here for linux */
+}
+
+void   ahc_platform_set_tags(struct ahc_softc *ahc,
+                             struct ahc_devinfo *devinfo, ahc_queue_alg);
+int    ahc_platform_abort_scbs(struct ahc_softc *ahc, int target,
+                               char channel, int lun, u_int tag,
+                               role_t role, uint32_t status);
+void   ahc_linux_isr(int irq, void *dev_id, struct pt_regs * regs);
+void   ahc_platform_flushwork(struct ahc_softc *ahc);
+int    ahc_softc_comp(struct ahc_softc *, struct ahc_softc *);
+void   ahc_done(struct ahc_softc*, struct scb*);
+void   ahc_send_async(struct ahc_softc *, char channel,
+                      u_int target, u_int lun, ac_code, void *);
+void   ahc_print_path(struct ahc_softc *, struct scb *);
+void   ahc_platform_dump_card_state(struct ahc_softc *ahc);
+
+#ifdef CONFIG_PCI
+#define AHC_PCI_CONFIG 1
+#else
+#define AHC_PCI_CONFIG 0
+#endif
+#define bootverbose aic7xxx_verbose
+extern int aic7xxx_verbose;
+#endif /* _AIC7XXX_LINUX_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c b/xen/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c
new file mode 100644 (file)
index 0000000..95cc64c
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * Linux driver attachment glue for PCI based controllers.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c#32 $
+ */
+
+#include "aic7xxx_osm.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+struct pci_device_id
+{
+};
+#endif
+
+static int     ahc_linux_pci_dev_probe(struct pci_dev *pdev,
+                                       const struct pci_device_id *ent);
+static int     ahc_linux_pci_reserve_io_region(struct ahc_softc *ahc,
+                                               u_long *base);
+#ifdef MMAPIO
+static int     ahc_linux_pci_reserve_mem_region(struct ahc_softc *ahc,
+                                                u_long *bus_addr,
+                                                uint8_t **maddr);
+#endif /* MMAPIO */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+static void    ahc_linux_pci_dev_remove(struct pci_dev *pdev);
+
+/* We do our own ID filtering.  So, grab all SCSI storage class devices. */
+static struct pci_device_id ahc_linux_pci_id_table[] = {
+       {
+               0x9004, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+               PCI_CLASS_STORAGE_SCSI << 8, 0xFFFF00, 0
+       },
+       {
+               0x9005, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+               PCI_CLASS_STORAGE_SCSI << 8, 0xFFFF00, 0
+       },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, ahc_linux_pci_id_table);
+
+struct pci_driver aic7xxx_pci_driver = {
+       name:           "aic7xxx",
+       probe:          ahc_linux_pci_dev_probe,
+       remove:         ahc_linux_pci_dev_remove,
+       id_table:       ahc_linux_pci_id_table
+};
+
+static void
+ahc_linux_pci_dev_remove(struct pci_dev *pdev)
+{
+       struct ahc_softc *ahc;
+       u_long l;
+
+       /*
+        * We should be able to just perform
+        * the free directly, but check our
+        * list for extra sanity.
+        */
+       ahc_list_lock(&l);
+       ahc = ahc_find_softc((struct ahc_softc *)pdev->driver_data);
+       if (ahc != NULL) {
+               u_long s;
+
+               ahc_lock(ahc, &s);
+               ahc_intr_enable(ahc, FALSE);
+               ahc_unlock(ahc, &s);
+               ahc_free(ahc);
+       }
+       ahc_list_unlock(&l);
+}
+#endif /* !LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) */
+
+static int
+ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       char             buf[80];
+       struct           ahc_softc *ahc;
+       ahc_dev_softc_t  pci;
+       struct           ahc_pci_identity *entry;
+       char            *name;
+       int              error;
+
+       /*
+        * Some BIOSen report the same device multiple times.
+        */
+       TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+               struct pci_dev *probed_pdev;
+
+               probed_pdev = ahc->dev_softc;
+               if (probed_pdev->bus->number == pdev->bus->number
+                && probed_pdev->devfn == pdev->devfn)
+                       break;
+       }
+       if (ahc != NULL) {
+               /* Skip duplicate. */
+               return (-ENODEV);
+       }
+
+       pci = pdev;
+       entry = ahc_find_pci_device(pci);
+       if (entry == NULL)
+               return (-ENODEV);
+
+       /*
+        * Allocate a softc for this card and
+        * set it up for attachment by our
+        * common detect routine.
+        */
+       sprintf(buf, "ahc_pci:%d:%d:%d",
+               ahc_get_pci_bus(pci),
+               ahc_get_pci_slot(pci),
+               ahc_get_pci_function(pci));
+       name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+       if (name == NULL)
+               return (-ENOMEM);
+       strcpy(name, buf);
+       ahc = ahc_alloc(NULL, name);
+       if (ahc == NULL)
+               return (-ENOMEM);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+       if (pci_enable_device(pdev)) {
+               ahc_free(ahc);
+               return (-ENODEV);
+       }
+       pci_set_master(pdev);
+
+       if (sizeof(bus_addr_t) > 4
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3)
+        && ahc_linux_get_memsize() > 0x80000000
+        && pci_set_dma_mask(pdev, 0x7FFFFFFFFFULL) == 0) {
+#else
+        && ahc_linux_get_memsize() > 0x80000000) {
+
+               ahc->dev_softc->dma_mask = 
+                   (bus_addr_t)(0x7FFFFFFFFFULL & (bus_addr_t)~0);
+#endif
+               ahc->flags |= AHC_39BIT_ADDRESSING;
+               ahc->platform_data->hw_dma_mask =
+                   (bus_addr_t)(0x7FFFFFFFFFULL & (bus_addr_t)~0);
+       }
+#endif
+       ahc->dev_softc = pci;
+       error = ahc_pci_config(ahc, entry);
+       if (error != 0) {
+               ahc_free(ahc);
+               return (-error);
+       }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+       pci_set_drvdata(pdev, ahc);
+       if (aic7xxx_detect_complete)
+               ahc_linux_register_host(ahc, aic7xxx_driver_template);
+#endif
+       return (0);
+}
+
+int
+ahc_linux_pci_probe(Scsi_Host_Template *template)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+       return (pci_module_init(&aic7xxx_pci_driver));
+#else
+       struct pci_dev *pdev;
+       u_int class;
+       int found;
+
+       /* If we don't have a PCI bus, we can't find any adapters. */
+       if (pci_present() == 0)
+               return (0);
+
+       found = 0;
+       pdev = NULL;
+       class = PCI_CLASS_STORAGE_SCSI << 8;
+       while ((pdev = pci_find_class(class, pdev)) != NULL) {
+               ahc_dev_softc_t pci;
+               int error;
+
+               pci = pdev;
+               error = ahc_linux_pci_dev_probe(pdev, /*pci_devid*/NULL);
+               if (error == 0)
+                       found++;
+       }
+       return (found);
+#endif
+}
+
+static int
+ahc_linux_pci_reserve_io_region(struct ahc_softc *ahc, u_long *base)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       *base = pci_resource_start(ahc->dev_softc, 0);
+#else
+       *base = ahc_pci_read_config(ahc->dev_softc, PCIR_MAPS, 4);
+       *base &= PCI_BASE_ADDRESS_IO_MASK;
+#endif
+       if (*base == 0)
+               return (ENOMEM);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+       if (check_region(*base, 256) != 0)
+               return (ENOMEM);
+       else
+               request_region(*base, 256, "aic7xxx");
+#else
+       if (request_region(*base, 256, "aic7xxx") == 0)
+               return (ENOMEM);
+#endif
+       return (0);
+}
+
+#ifdef MMAPIO
+static int
+ahc_linux_pci_reserve_mem_region(struct ahc_softc *ahc,
+                                u_long *bus_addr,
+                                uint8_t **maddr)
+{
+       u_long  start;
+       u_long  base_page;
+       u_long  base_offset;
+       int     error;
+
+       error = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+       start = pci_resource_start(ahc->dev_softc, 1);
+       base_page = start & PAGE_MASK;
+       base_offset = start - base_page;
+#else
+       start = ahc_pci_read_config(ahc->dev_softc, PCIR_MAPS+4, 4);
+       base_offset = start & PCI_BASE_ADDRESS_MEM_MASK;
+       base_page = base_offset & PAGE_MASK;
+       base_offset -= base_page;
+#endif
+       if (start != 0) {
+               *bus_addr = start;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+               if (request_mem_region(start, 0x1000, "aic7xxx") == 0)
+                       error = ENOMEM;
+#endif
+               if (error == 0) {
+                       *maddr = ioremap_nocache(base_page, base_offset + 256);
+                       if (*maddr == NULL) {
+                               error = ENOMEM;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+                               release_mem_region(start, 0x1000);
+#endif
+                       } else
+                               *maddr += base_offset;
+               }
+       } else
+               error = ENOMEM;
+       return (error);
+}
+#endif /* MMAPIO */
+
+int
+ahc_pci_map_registers(struct ahc_softc *ahc)
+{
+       uint32_t command;
+       u_long   base;
+       uint8_t *maddr;
+       int      error;
+
+       /*
+        * If its allowed, we prefer memory mapped access.
+        */
+       command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, 4);
+       command &= ~(PCIM_CMD_PORTEN|PCIM_CMD_MEMEN);
+       base = 0;
+       maddr = NULL;
+#ifdef MMAPIO
+       error = ahc_linux_pci_reserve_mem_region(ahc, &base, &maddr);
+       if (error == 0) {
+               ahc->platform_data->mem_busaddr = base;
+               ahc->tag = BUS_SPACE_MEMIO;
+               ahc->bsh.maddr = maddr;
+               ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND,
+                                    command | PCIM_CMD_MEMEN, 4);
+
+               /*
+                * Do a quick test to see if memory mapped
+                * I/O is functioning correctly.
+                */
+               if (ahc_inb(ahc, HCNTRL) == 0xFF) {
+
+                       printf("aic7xxx: PCI Device %d:%d:%d "
+                              "failed memory mapped test\n",
+                              ahc_get_pci_bus(ahc->dev_softc),
+                              ahc_get_pci_slot(ahc->dev_softc),
+                              ahc_get_pci_function(ahc->dev_softc));
+                       iounmap((void *)((u_long)maddr & PAGE_MASK));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+                       release_mem_region(ahc->platform_data->mem_busaddr,
+                                          0x1000);
+#endif
+                       ahc->bsh.maddr = NULL;
+                       maddr = NULL;
+               } else
+                       command |= PCIM_CMD_MEMEN;
+       } else {
+               printf("aic7xxx: PCI%d:%d:%d MEM region 0x%lx "
+                      "unavailable. Cannot memory map device.\n",
+                      ahc_get_pci_bus(ahc->dev_softc),
+                      ahc_get_pci_slot(ahc->dev_softc),
+                      ahc_get_pci_function(ahc->dev_softc),
+                      base);
+       }
+#endif /* MMAPIO */
+
+       /*
+        * We always prefer memory mapped access.
+        */
+       if (maddr == NULL) {
+
+               error = ahc_linux_pci_reserve_io_region(ahc, &base);
+               if (error == 0) {
+                       ahc->tag = BUS_SPACE_PIO;
+                       ahc->bsh.ioport = base;
+                       command |= PCIM_CMD_PORTEN;
+               } else {
+                       printf("aic7xxx: PCI%d:%d:%d IO region 0x%lx[0..255] "
+                              "unavailable. Cannot map device.\n",
+                              ahc_get_pci_bus(ahc->dev_softc),
+                              ahc_get_pci_slot(ahc->dev_softc),
+                              ahc_get_pci_function(ahc->dev_softc),
+                              base);
+               }
+       }
+       ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, 4);
+       return (error);
+}
+
+int
+ahc_pci_map_int(struct ahc_softc *ahc)
+{
+       int error;
+
+       error = request_irq(ahc->dev_softc->irq, ahc_linux_isr,
+                           SA_SHIRQ, "aic7xxx", ahc);
+       if (error == 0)
+               ahc->platform_data->irq = ahc->dev_softc->irq;
+       
+       return (-error);
+}
+
+void
+ahc_power_state_change(struct ahc_softc *ahc, ahc_power_state new_state)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+       pci_set_power_state(ahc->dev_softc, new_state);
+#else
+       uint32_t cap;
+       u_int cap_offset;
+
+       /*
+        * Traverse the capability list looking for
+        * the power management capability.
+        */
+       cap = 0;
+       cap_offset = ahc_pci_read_config(ahc->dev_softc,
+                                        PCIR_CAP_PTR, /*bytes*/1);
+       while (cap_offset != 0) {
+
+               cap = ahc_pci_read_config(ahc->dev_softc,
+                                         cap_offset, /*bytes*/4);
+               if ((cap & 0xFF) == 1
+                && ((cap >> 16) & 0x3) > 0) {
+                       uint32_t pm_control;
+
+                       pm_control = ahc_pci_read_config(ahc->dev_softc,
+                                                        cap_offset + 4,
+                                                        /*bytes*/4);
+                       pm_control &= ~0x3;
+                       pm_control |= new_state;
+                       ahc_pci_write_config(ahc->dev_softc,
+                                            cap_offset + 4,
+                                            pm_control, /*bytes*/2);
+                       break;
+               }
+               cap_offset = (cap >> 8) & 0xFF;
+       }
+#endif 
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_pci.c b/xen/drivers/scsi/aic7xxx/aic7xxx_pci.c
new file mode 100644 (file)
index 0000000..6108b4f
--- /dev/null
@@ -0,0 +1,2277 @@
+/*
+ * Product specific probe and attach routines for:
+ *      3940, 2940, aic7895, aic7890, aic7880,
+ *     aic7870, aic7860 and aic7850 SCSI controllers
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_pci.c#46 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_pci.c,v 1.2.2.14 2002/04/29 19:36:31 gibbs Exp $
+ */
+
+#ifdef __linux__
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#else
+#include <dev/aic7xxx/aic7xxx_osm.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aic7xxx_93cx6.h>
+#endif
+
+#define AHC_PCI_IOADDR PCIR_MAPS       /* I/O Address */
+#define AHC_PCI_MEMADDR        (PCIR_MAPS + 4) /* Mem I/O Address */
+
+static __inline uint64_t
+ahc_compose_id(u_int device, u_int vendor, u_int subdevice, u_int subvendor)
+{
+       uint64_t id;
+
+       id = subvendor
+          | (subdevice << 16)
+          | ((uint64_t)vendor << 32)
+          | ((uint64_t)device << 48);
+
+       return (id);
+}
+
+#define ID_ALL_MASK                    0xFFFFFFFFFFFFFFFFull
+#define ID_DEV_VENDOR_MASK             0xFFFFFFFF00000000ull
+#define ID_9005_GENERIC_MASK           0xFFF0FFFF00000000ull
+#define ID_9005_SISL_MASK              0x000FFFFF00000000ull
+#define ID_9005_SISL_ID                        0x0005900500000000ull
+#define ID_AIC7850                     0x5078900400000000ull
+#define ID_AHA_2902_04_10_15_20_30C    0x5078900478509004ull
+#define ID_AIC7855                     0x5578900400000000ull
+#define ID_AIC7859                     0x3860900400000000ull
+#define ID_AHA_2930CU                  0x3860900438699004ull
+#define ID_AIC7860                     0x6078900400000000ull
+#define ID_AIC7860C                    0x6078900478609004ull
+#define ID_AHA_1480A                   0x6075900400000000ull
+#define ID_AHA_2940AU_0                        0x6178900400000000ull
+#define ID_AHA_2940AU_1                        0x6178900478619004ull
+#define ID_AHA_2940AU_CN               0x2178900478219004ull
+#define ID_AHA_2930C_VAR               0x6038900438689004ull
+
+#define ID_AIC7870                     0x7078900400000000ull
+#define ID_AHA_2940                    0x7178900400000000ull
+#define ID_AHA_3940                    0x7278900400000000ull
+#define ID_AHA_398X                    0x7378900400000000ull
+#define ID_AHA_2944                    0x7478900400000000ull
+#define ID_AHA_3944                    0x7578900400000000ull
+#define ID_AHA_4944                    0x7678900400000000ull
+
+#define ID_AIC7880                     0x8078900400000000ull
+#define ID_AIC7880_B                   0x8078900478809004ull
+#define ID_AHA_2940U                   0x8178900400000000ull
+#define ID_AHA_3940U                   0x8278900400000000ull
+#define ID_AHA_2944U                   0x8478900400000000ull
+#define ID_AHA_3944U                   0x8578900400000000ull
+#define ID_AHA_398XU                   0x8378900400000000ull
+#define ID_AHA_4944U                   0x8678900400000000ull
+#define ID_AHA_2940UB                  0x8178900478819004ull
+#define ID_AHA_2930U                   0x8878900478889004ull
+#define ID_AHA_2940U_PRO               0x8778900478879004ull
+#define ID_AHA_2940U_CN                        0x0078900478009004ull
+
+#define ID_AIC7895                     0x7895900478959004ull
+#define ID_AIC7895_ARO                 0x7890900478939004ull
+#define ID_AIC7895_ARO_MASK            0xFFF0FFFFFFFFFFFFull
+#define ID_AHA_2940U_DUAL              0x7895900478919004ull
+#define ID_AHA_3940AU                  0x7895900478929004ull
+#define ID_AHA_3944AU                  0x7895900478949004ull
+
+#define ID_AIC7890                     0x001F9005000F9005ull
+#define ID_AIC7890_ARO                 0x00139005000F9005ull
+#define ID_AAA_131U2                   0x0013900500039005ull
+#define ID_AHA_2930U2                  0x0011900501819005ull
+#define ID_AHA_2940U2B                 0x00109005A1009005ull
+#define ID_AHA_2940U2_OEM              0x0010900521809005ull
+#define ID_AHA_2940U2                  0x00109005A1809005ull
+#define ID_AHA_2950U2B                 0x00109005E1009005ull
+
+#define ID_AIC7892                     0x008F9005FFFF9005ull
+#define ID_AIC7892_ARO                 0x00839005FFFF9005ull
+#define ID_AHA_29160                   0x00809005E2A09005ull
+#define ID_AHA_29160_CPQ               0x00809005E2A00E11ull
+#define ID_AHA_29160N                  0x0080900562A09005ull
+#define ID_AHA_29160C                  0x0080900562209005ull
+#define ID_AHA_29160B                  0x00809005E2209005ull
+#define ID_AHA_19160B                  0x0081900562A19005ull
+
+#define ID_AIC7896                     0x005F9005FFFF9005ull
+#define ID_AIC7896_ARO                 0x00539005FFFF9005ull
+#define ID_AHA_3950U2B_0               0x00509005FFFF9005ull
+#define ID_AHA_3950U2B_1               0x00509005F5009005ull
+#define ID_AHA_3950U2D_0               0x00519005FFFF9005ull
+#define ID_AHA_3950U2D_1               0x00519005B5009005ull
+
+#define ID_AIC7899                     0x00CF9005FFFF9005ull
+#define ID_AIC7899_ARO                 0x00C39005FFFF9005ull
+#define ID_AHA_3960D                   0x00C09005F6209005ull
+#define ID_AHA_3960D_CPQ               0x00C09005F6200E11ull
+
+#define ID_AIC7810                     0x1078900400000000ull
+#define ID_AIC7815                     0x7815900400000000ull
+
+#define DEVID_9005_TYPE(id) ((id) & 0xF)
+#define                DEVID_9005_TYPE_HBA             0x0     /* Standard Card */
+#define                DEVID_9005_TYPE_AAA             0x3     /* RAID Card */
+#define                DEVID_9005_TYPE_SISL            0x5     /* Container ROMB */
+#define                DEVID_9005_TYPE_MB              0xF     /* On Motherboard */
+
+#define DEVID_9005_MAXRATE(id) (((id) & 0x30) >> 4)
+#define                DEVID_9005_MAXRATE_U160         0x0
+#define                DEVID_9005_MAXRATE_ULTRA2       0x1
+#define                DEVID_9005_MAXRATE_ULTRA        0x2
+#define                DEVID_9005_MAXRATE_FAST         0x3
+
+#define DEVID_9005_MFUNC(id) (((id) & 0x40) >> 6)
+
+#define DEVID_9005_CLASS(id) (((id) & 0xFF00) >> 8)
+#define                DEVID_9005_CLASS_SPI            0x0     /* Parallel SCSI */
+
+#define SUBID_9005_TYPE(id) ((id) & 0xF)
+#define                SUBID_9005_TYPE_MB              0xF     /* On Motherboard */
+#define                SUBID_9005_TYPE_CARD            0x0     /* Standard Card */
+#define                SUBID_9005_TYPE_LCCARD          0x1     /* Low Cost Card */
+#define                SUBID_9005_TYPE_RAID            0x3     /* Combined with Raid */
+
+#define SUBID_9005_TYPE_KNOWN(id)                      \
+         ((((id) & 0xF) == SUBID_9005_TYPE_MB)         \
+       || (((id) & 0xF) == SUBID_9005_TYPE_CARD)       \
+       || (((id) & 0xF) == SUBID_9005_TYPE_LCCARD)     \
+       || (((id) & 0xF) == SUBID_9005_TYPE_RAID))
+
+#define SUBID_9005_MAXRATE(id) (((id) & 0x30) >> 4)
+#define                SUBID_9005_MAXRATE_ULTRA2       0x0
+#define                SUBID_9005_MAXRATE_ULTRA        0x1
+#define                SUBID_9005_MAXRATE_U160         0x2
+#define                SUBID_9005_MAXRATE_RESERVED     0x3
+
+#define SUBID_9005_SEEPTYPE(id)                                                \
+       ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)                    \
+        ? ((id) & 0xC0) >> 6                                           \
+        : ((id) & 0x300) >> 8)
+#define                SUBID_9005_SEEPTYPE_NONE        0x0
+#define                SUBID_9005_SEEPTYPE_1K          0x1
+#define                SUBID_9005_SEEPTYPE_2K_4K       0x2
+#define                SUBID_9005_SEEPTYPE_RESERVED    0x3
+#define SUBID_9005_AUTOTERM(id)                                                \
+       ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)                    \
+        ? (((id) & 0x400) >> 10) == 0                                  \
+        : (((id) & 0x40) >> 6) == 0)
+
+#define SUBID_9005_NUMCHAN(id)                                         \
+       ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)                    \
+        ? ((id) & 0x300) >> 8                                          \
+        : ((id) & 0xC00) >> 10)
+
+#define SUBID_9005_LEGACYCONN(id)                                      \
+       ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)                    \
+        ? 0                                                            \
+        : ((id) & 0x80) >> 7)
+
+#define SUBID_9005_MFUNCENB(id)                                                \
+       ((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)                    \
+        ? ((id) & 0x800) >> 11                                         \
+        : ((id) & 0x1000) >> 12)
+/*
+ * Informational only. Should use chip register to be
+ * certain, but may be use in identification strings.
+ */
+#define SUBID_9005_CARD_SCSIWIDTH_MASK 0x2000
+#define SUBID_9005_CARD_PCIWIDTH_MASK  0x4000
+#define SUBID_9005_CARD_SEDIFF_MASK    0x8000
+
+static ahc_device_setup_t ahc_aic785X_setup;
+static ahc_device_setup_t ahc_aic7860_setup;
+static ahc_device_setup_t ahc_apa1480_setup;
+static ahc_device_setup_t ahc_aic7870_setup;
+static ahc_device_setup_t ahc_aha394X_setup;
+static ahc_device_setup_t ahc_aha494X_setup;
+static ahc_device_setup_t ahc_aha398X_setup;
+static ahc_device_setup_t ahc_aic7880_setup;
+static ahc_device_setup_t ahc_aha2940Pro_setup;
+static ahc_device_setup_t ahc_aha394XU_setup;
+static ahc_device_setup_t ahc_aha398XU_setup;
+static ahc_device_setup_t ahc_aic7890_setup;
+static ahc_device_setup_t ahc_aic7892_setup;
+static ahc_device_setup_t ahc_aic7895_setup;
+static ahc_device_setup_t ahc_aic7896_setup;
+static ahc_device_setup_t ahc_aic7899_setup;
+static ahc_device_setup_t ahc_aha29160C_setup;
+static ahc_device_setup_t ahc_raid_setup;
+static ahc_device_setup_t ahc_aha394XX_setup;
+static ahc_device_setup_t ahc_aha494XX_setup;
+static ahc_device_setup_t ahc_aha398XX_setup;
+
+struct ahc_pci_identity ahc_pci_ident_table [] =
+{
+       /* aic7850 based controllers */
+       {
+               ID_AHA_2902_04_10_15_20_30C,
+               ID_ALL_MASK,
+               "Adaptec 2902/04/10/15/20/30C SCSI adapter",
+               ahc_aic785X_setup
+       },
+       /* aic7860 based controllers */
+       {
+               ID_AHA_2930CU,
+               ID_ALL_MASK,
+               "Adaptec 2930CU SCSI adapter",
+               ahc_aic7860_setup
+       },
+       {
+               ID_AHA_1480A & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec 1480A Ultra SCSI adapter",
+               ahc_apa1480_setup
+       },
+       {
+               ID_AHA_2940AU_0 & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec 2940A Ultra SCSI adapter",
+               ahc_aic7860_setup
+       },
+       {
+               ID_AHA_2940AU_CN & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec 2940A/CN Ultra SCSI adapter",
+               ahc_aic7860_setup
+       },
+       {
+               ID_AHA_2930C_VAR & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec 2930C Ultra SCSI adapter (VAR)",
+               ahc_aic7860_setup
+       },
+       /* aic7870 based controllers */
+       {
+               ID_AHA_2940,
+               ID_ALL_MASK,
+               "Adaptec 2940 SCSI adapter",
+               ahc_aic7870_setup
+       },
+       {
+               ID_AHA_3940,
+               ID_ALL_MASK,
+               "Adaptec 3940 SCSI adapter",
+               ahc_aha394X_setup
+       },
+       {
+               ID_AHA_398X,
+               ID_ALL_MASK,
+               "Adaptec 398X SCSI RAID adapter",
+               ahc_aha398X_setup
+       },
+       {
+               ID_AHA_2944,
+               ID_ALL_MASK,
+               "Adaptec 2944 SCSI adapter",
+               ahc_aic7870_setup
+       },
+       {
+               ID_AHA_3944,
+               ID_ALL_MASK,
+               "Adaptec 3944 SCSI adapter",
+               ahc_aha394X_setup
+       },
+       {
+               ID_AHA_4944,
+               ID_ALL_MASK,
+               "Adaptec 4944 SCSI adapter",
+               ahc_aha494X_setup
+       },
+       /* aic7880 based controllers */
+       {
+               ID_AHA_2940U & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec 2940 Ultra SCSI adapter",
+               ahc_aic7880_setup
+       },
+       {
+               ID_AHA_3940U & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec 3940 Ultra SCSI adapter",
+               ahc_aha394XU_setup
+       },
+       {
+               ID_AHA_2944U & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec 2944 Ultra SCSI adapter",
+               ahc_aic7880_setup
+       },
+       {
+               ID_AHA_3944U & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec 3944 Ultra SCSI adapter",
+               ahc_aha394XU_setup
+       },
+       {
+               ID_AHA_398XU & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec 398X Ultra SCSI RAID adapter",
+               ahc_aha398XU_setup
+       },
+       {
+               /*
+                * XXX Don't know the slot numbers
+                * so we can't identify channels
+                */
+               ID_AHA_4944U & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec 4944 Ultra SCSI adapter",
+               ahc_aic7880_setup
+       },
+       {
+               ID_AHA_2930U & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec 2930 Ultra SCSI adapter",
+               ahc_aic7880_setup
+       },
+       {
+               ID_AHA_2940U_PRO & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec 2940 Pro Ultra SCSI adapter",
+               ahc_aha2940Pro_setup
+       },
+       {
+               ID_AHA_2940U_CN & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec 2940/CN Ultra SCSI adapter",
+               ahc_aic7880_setup
+       },
+       /* Ignore all SISL (AAC on MB) based controllers. */
+       {
+               ID_9005_SISL_ID,
+               ID_9005_SISL_MASK,
+               NULL,
+               NULL
+       },
+       /* aic7890 based controllers */
+       {
+               ID_AHA_2930U2,
+               ID_ALL_MASK,
+               "Adaptec 2930 Ultra2 SCSI adapter",
+               ahc_aic7890_setup
+       },
+       {
+               ID_AHA_2940U2B,
+               ID_ALL_MASK,
+               "Adaptec 2940B Ultra2 SCSI adapter",
+               ahc_aic7890_setup
+       },
+       {
+               ID_AHA_2940U2_OEM,
+               ID_ALL_MASK,
+               "Adaptec 2940 Ultra2 SCSI adapter (OEM)",
+               ahc_aic7890_setup
+       },
+       {
+               ID_AHA_2940U2,
+               ID_ALL_MASK,
+               "Adaptec 2940 Ultra2 SCSI adapter",
+               ahc_aic7890_setup
+       },
+       {
+               ID_AHA_2950U2B,
+               ID_ALL_MASK,
+               "Adaptec 2950 Ultra2 SCSI adapter",
+               ahc_aic7890_setup
+       },
+       {
+               ID_AIC7890_ARO,
+               ID_ALL_MASK,
+               "Adaptec aic7890/91 Ultra2 SCSI adapter (ARO)",
+               ahc_aic7890_setup
+       },
+       {
+               ID_AAA_131U2,
+               ID_ALL_MASK,
+               "Adaptec AAA-131 Ultra2 RAID adapter",
+               ahc_aic7890_setup
+       },
+       /* aic7892 based controllers */
+       {
+               ID_AHA_29160,
+               ID_ALL_MASK,
+               "Adaptec 29160 Ultra160 SCSI adapter",
+               ahc_aic7892_setup
+       },
+       {
+               ID_AHA_29160_CPQ,
+               ID_ALL_MASK,
+               "Adaptec (Compaq OEM) 29160 Ultra160 SCSI adapter",
+               ahc_aic7892_setup
+       },
+       {
+               ID_AHA_29160N,
+               ID_ALL_MASK,
+               "Adaptec 29160N Ultra160 SCSI adapter",
+               ahc_aic7892_setup
+       },
+       {
+               ID_AHA_29160C,
+               ID_ALL_MASK,
+               "Adaptec 29160C Ultra160 SCSI adapter",
+               ahc_aha29160C_setup
+       },
+       {
+               ID_AHA_29160B,
+               ID_ALL_MASK,
+               "Adaptec 29160B Ultra160 SCSI adapter",
+               ahc_aic7892_setup
+       },
+       {
+               ID_AHA_19160B,
+               ID_ALL_MASK,
+               "Adaptec 19160B Ultra160 SCSI adapter",
+               ahc_aic7892_setup
+       },
+       {
+               ID_AIC7892_ARO,
+               ID_ALL_MASK,
+               "Adaptec aic7892 Ultra160 SCSI adapter (ARO)",
+               ahc_aic7892_setup
+       },
+       /* aic7895 based controllers */ 
+       {
+               ID_AHA_2940U_DUAL,
+               ID_ALL_MASK,
+               "Adaptec 2940/DUAL Ultra SCSI adapter",
+               ahc_aic7895_setup
+       },
+       {
+               ID_AHA_3940AU,
+               ID_ALL_MASK,
+               "Adaptec 3940A Ultra SCSI adapter",
+               ahc_aic7895_setup
+       },
+       {
+               ID_AHA_3944AU,
+               ID_ALL_MASK,
+               "Adaptec 3944A Ultra SCSI adapter",
+               ahc_aic7895_setup
+       },
+       {
+               ID_AIC7895_ARO,
+               ID_AIC7895_ARO_MASK,
+               "Adaptec aic7895 Ultra SCSI adapter (ARO)",
+               ahc_aic7895_setup
+       },
+       /* aic7896/97 based controllers */      
+       {
+               ID_AHA_3950U2B_0,
+               ID_ALL_MASK,
+               "Adaptec 3950B Ultra2 SCSI adapter",
+               ahc_aic7896_setup
+       },
+       {
+               ID_AHA_3950U2B_1,
+               ID_ALL_MASK,
+               "Adaptec 3950B Ultra2 SCSI adapter",
+               ahc_aic7896_setup
+       },
+       {
+               ID_AHA_3950U2D_0,
+               ID_ALL_MASK,
+               "Adaptec 3950D Ultra2 SCSI adapter",
+               ahc_aic7896_setup
+       },
+       {
+               ID_AHA_3950U2D_1,
+               ID_ALL_MASK,
+               "Adaptec 3950D Ultra2 SCSI adapter",
+               ahc_aic7896_setup
+       },
+       {
+               ID_AIC7896_ARO,
+               ID_ALL_MASK,
+               "Adaptec aic7896/97 Ultra2 SCSI adapter (ARO)",
+               ahc_aic7896_setup
+       },
+       /* aic7899 based controllers */ 
+       {
+               ID_AHA_3960D,
+               ID_ALL_MASK,
+               "Adaptec 3960D Ultra160 SCSI adapter",
+               ahc_aic7899_setup
+       },
+       {
+               ID_AHA_3960D_CPQ,
+               ID_ALL_MASK,
+               "Adaptec (Compaq OEM) 3960D Ultra160 SCSI adapter",
+               ahc_aic7899_setup
+       },
+       {
+               ID_AIC7899_ARO,
+               ID_ALL_MASK,
+               "Adaptec aic7899 Ultra160 SCSI adapter (ARO)",
+               ahc_aic7899_setup
+       },
+       /* Generic chip probes for devices we don't know 'exactly' */
+       {
+               ID_AIC7850 & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec aic7850 SCSI adapter",
+               ahc_aic785X_setup
+       },
+       {
+               ID_AIC7855 & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec aic7855 SCSI adapter",
+               ahc_aic785X_setup
+       },
+       {
+               ID_AIC7859 & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec aic7859 SCSI adapter",
+               ahc_aic7860_setup
+       },
+       {
+               ID_AIC7860 & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec aic7860 Ultra SCSI adapter",
+               ahc_aic7860_setup
+       },
+       {
+               ID_AIC7870 & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec aic7870 SCSI adapter",
+               ahc_aic7870_setup
+       },
+       {
+               ID_AIC7880 & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec aic7880 Ultra SCSI adapter",
+               ahc_aic7880_setup
+       },
+       {
+               ID_AIC7890 & ID_9005_GENERIC_MASK,
+               ID_9005_GENERIC_MASK,
+               "Adaptec aic7890/91 Ultra2 SCSI adapter",
+               ahc_aic7890_setup
+       },
+       {
+               ID_AIC7892 & ID_9005_GENERIC_MASK,
+               ID_9005_GENERIC_MASK,
+               "Adaptec aic7892 Ultra160 SCSI adapter",
+               ahc_aic7892_setup
+       },
+       {
+               ID_AIC7895 & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec aic7895 Ultra SCSI adapter",
+               ahc_aic7895_setup
+       },
+       {
+               ID_AIC7896 & ID_9005_GENERIC_MASK,
+               ID_9005_GENERIC_MASK,
+               "Adaptec aic7896/97 Ultra2 SCSI adapter",
+               ahc_aic7896_setup
+       },
+       {
+               ID_AIC7899 & ID_9005_GENERIC_MASK,
+               ID_9005_GENERIC_MASK,
+               "Adaptec aic7899 Ultra160 SCSI adapter",
+               ahc_aic7899_setup
+       },
+       {
+               ID_AIC7810 & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec aic7810 RAID memory controller",
+               ahc_raid_setup
+       },
+       {
+               ID_AIC7815 & ID_DEV_VENDOR_MASK,
+               ID_DEV_VENDOR_MASK,
+               "Adaptec aic7815 RAID memory controller",
+               ahc_raid_setup
+       }
+};
+
+const u_int ahc_num_pci_devs = NUM_ELEMENTS(ahc_pci_ident_table);
+               
+#define AHC_394X_SLOT_CHANNEL_A        4
+#define AHC_394X_SLOT_CHANNEL_B        5
+
+#define AHC_398X_SLOT_CHANNEL_A        4
+#define AHC_398X_SLOT_CHANNEL_B        8
+#define AHC_398X_SLOT_CHANNEL_C        12
+
+#define AHC_494X_SLOT_CHANNEL_A        4
+#define AHC_494X_SLOT_CHANNEL_B        5
+#define AHC_494X_SLOT_CHANNEL_C        6
+#define AHC_494X_SLOT_CHANNEL_D        7
+
+#define        DEVCONFIG               0x40
+#define                SCBSIZE32       0x00010000ul    /* aic789X only */
+#define                REXTVALID       0x00001000ul    /* ultra cards only */
+#define                MPORTMODE       0x00000400ul    /* aic7870+ only */
+#define                RAMPSM          0x00000200ul    /* aic7870+ only */
+#define                VOLSENSE        0x00000100ul
+#define                PCI64BIT        0x00000080ul    /* 64Bit PCI bus (Ultra2 Only)*/
+#define                SCBRAMSEL       0x00000080ul
+#define                MRDCEN          0x00000040ul
+#define                EXTSCBTIME      0x00000020ul    /* aic7870 only */
+#define                EXTSCBPEN       0x00000010ul    /* aic7870 only */
+#define                BERREN          0x00000008ul
+#define                DACEN           0x00000004ul
+#define                STPWLEVEL       0x00000002ul
+#define                DIFACTNEGEN     0x00000001ul    /* aic7870 only */
+
+#define        CSIZE_LATTIME           0x0c
+#define                CACHESIZE       0x0000003ful    /* only 5 bits */
+#define                LATTIME         0x0000ff00ul
+
+static int ahc_9005_subdevinfo_valid(uint16_t vendor, uint16_t device,
+                                    uint16_t subvendor, uint16_t subdevice);
+static int ahc_ext_scbram_present(struct ahc_softc *ahc);
+static void ahc_scbram_config(struct ahc_softc *ahc, int enable,
+                                 int pcheck, int fast, int large);
+static void ahc_probe_ext_scbram(struct ahc_softc *ahc);
+static void check_extport(struct ahc_softc *ahc, u_int *sxfrctl1);
+static void ahc_parse_pci_eeprom(struct ahc_softc *ahc,
+                                struct seeprom_config *sc);
+static void configure_termination(struct ahc_softc *ahc,
+                                 struct seeprom_descriptor *sd,
+                                 u_int adapter_control,
+                                 u_int *sxfrctl1);
+
+static void ahc_new_term_detect(struct ahc_softc *ahc,
+                               int *enableSEC_low,
+                               int *enableSEC_high,
+                               int *enablePRI_low,
+                               int *enablePRI_high,
+                               int *eeprom_present);
+static void aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+                                int *internal68_present,
+                                int *externalcable_present,
+                                int *eeprom_present);
+static void aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+                                int *externalcable_present,
+                                int *eeprom_present);
+static void write_brdctl(struct ahc_softc *ahc, uint8_t value);
+static uint8_t read_brdctl(struct ahc_softc *ahc);
+
+static int
+ahc_9005_subdevinfo_valid(uint16_t device, uint16_t vendor,
+                         uint16_t subdevice, uint16_t subvendor)
+{
+       int result;
+
+       /* Default to invalid. */
+       result = 0;
+       if (vendor == 0x9005
+        && subvendor == 0x9005
+         && subdevice != device
+         && SUBID_9005_TYPE_KNOWN(subdevice) != 0) {
+
+               switch (SUBID_9005_TYPE(subdevice)) {
+               case SUBID_9005_TYPE_MB:
+                       break;
+               case SUBID_9005_TYPE_CARD:
+               case SUBID_9005_TYPE_LCCARD:
+                       /*
+                        * Currently only trust Adaptec cards to
+                        * get the sub device info correct.
+                        */
+                       if (DEVID_9005_TYPE(device) == DEVID_9005_TYPE_HBA)
+                               result = 1;
+                       break;
+               case SUBID_9005_TYPE_RAID:
+                       break;
+               default:
+                       break;
+               }
+       }
+       return (result);
+}
+
+struct ahc_pci_identity *
+ahc_find_pci_device(ahc_dev_softc_t pci)
+{
+       uint64_t  full_id;
+       uint16_t  device;
+       uint16_t  vendor;
+       uint16_t  subdevice;
+       uint16_t  subvendor;
+       struct    ahc_pci_identity *entry;
+       u_int     i;
+
+       vendor = ahc_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2);
+       device = ahc_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2);
+       subvendor = ahc_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2);
+       subdevice = ahc_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2);
+       full_id = ahc_compose_id(device,
+                                vendor,
+                                subdevice,
+                                subvendor);
+
+       /*
+        * If the second function is not hooked up, ignore it.
+        * Unfortunately, not all MB vendors implement the
+        * subdevice ID as per the Adaptec spec, so do our best
+        * to sanity check it prior to accepting the subdevice
+        * ID as valid.
+        */
+       if (ahc_get_pci_function(pci) > 0
+        && ahc_9005_subdevinfo_valid(vendor, device, subvendor, subdevice)
+        && SUBID_9005_MFUNCENB(subdevice) == 0)
+               return (NULL);
+
+       for (i = 0; i < ahc_num_pci_devs; i++) {
+               entry = &ahc_pci_ident_table[i];
+               if (entry->full_id == (full_id & entry->id_mask)) {
+                       /* Honor exclusion entries. */
+                       if (entry->name == NULL)
+                               return (NULL);
+                       return (entry);
+               }
+       }
+       return (NULL);
+}
+
+int
+ahc_pci_config(struct ahc_softc *ahc, struct ahc_pci_identity *entry)
+{
+       struct scb_data *shared_scb_data;
+       u_long           l;
+       u_long           s;
+       u_int            command;
+       u_int            our_id = 0;
+       u_int            sxfrctl1;
+       u_int            scsiseq;
+       u_int            dscommand0;
+       int              error;
+       uint8_t          sblkctl;
+
+       shared_scb_data = NULL;
+       error = entry->setup(ahc);
+       if (error != 0)
+               return (error);
+       ahc->chip |= AHC_PCI;
+       ahc->description = entry->name;
+
+       ahc_power_state_change(ahc, AHC_POWER_STATE_D0);
+
+       error = ahc_pci_map_registers(ahc);
+       if (error != 0)
+               return (error);
+
+       /*
+        * Before we continue probing the card, ensure that
+        * its interrupts are *disabled*.  We don't want
+        * a misstep to hang the machine in an interrupt
+        * storm.
+        */
+       ahc_intr_enable(ahc, FALSE);
+
+       /*
+        * If we need to support high memory, enable dual
+        * address cycles.  This bit must be set to enable
+        * high address bit generation even if we are on a
+        * 64bit bus (PCI64BIT set in devconfig).
+        */
+       if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+               uint32_t devconfig;
+
+               if (bootverbose)
+                       printf("%s: Enabling 39Bit Addressing\n",
+                              ahc_name(ahc));
+               devconfig = ahc_pci_read_config(ahc->dev_softc,
+                                               DEVCONFIG, /*bytes*/4);
+               devconfig |= DACEN;
+               ahc_pci_write_config(ahc->dev_softc, DEVCONFIG,
+                                    devconfig, /*bytes*/4);
+       }
+       
+       /* Ensure busmastering is enabled */
+       command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, /*bytes*/1);
+       command |= PCIM_CMD_BUSMASTEREN;
+       ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, /*bytes*/1);
+
+       /* On all PCI adapters, we allow SCB paging */
+       ahc->flags |= AHC_PAGESCBS;
+
+       error = ahc_softc_init(ahc);
+       if (error != 0)
+               return (error);
+
+       ahc->bus_intr = ahc_pci_intr;
+
+       /* Remeber how the card was setup in case there is no SEEPROM */
+       if ((ahc_inb(ahc, HCNTRL) & POWRDN) == 0) {
+               ahc_pause(ahc);
+               if ((ahc->features & AHC_ULTRA2) != 0)
+                       our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
+               else
+                       our_id = ahc_inb(ahc, SCSIID) & OID;
+               sxfrctl1 = ahc_inb(ahc, SXFRCTL1) & STPWEN;
+               scsiseq = ahc_inb(ahc, SCSISEQ);
+       } else {
+               sxfrctl1 = STPWEN;
+               our_id = 7;
+               scsiseq = 0;
+       }
+
+       error = ahc_reset(ahc);
+       if (error != 0)
+               return (ENXIO);
+
+       if ((ahc->features & AHC_DT) != 0) {
+               u_int sfunct;
+
+               /* Perform ALT-Mode Setup */
+               sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+               ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+               ahc_outb(ahc, OPTIONMODE,
+                        OPTIONMODE_DEFAULTS|AUTOACKEN|BUSFREEREV|EXPPHASEDIS);
+               ahc_outb(ahc, SFUNCT, sfunct);
+
+               /* Normal mode setup */
+               ahc_outb(ahc, CRCCONTROL1, CRCVALCHKEN|CRCENDCHKEN|CRCREQCHKEN
+                                         |TARGCRCENDEN);
+       }
+
+       dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+       dscommand0 |= MPARCKEN|CACHETHEN;
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+
+               /*
+                * DPARCKEN doesn't work correctly on
+                * some MBs so don't use it.
+                */
+               dscommand0 &= ~DPARCKEN;
+       }
+
+       /*
+        * Handle chips that must have cache line
+        * streaming (dis/en)abled.
+        */
+       if ((ahc->bugs & AHC_CACHETHEN_DIS_BUG) != 0)
+               dscommand0 |= CACHETHEN;
+
+       if ((ahc->bugs & AHC_CACHETHEN_BUG) != 0)
+               dscommand0 &= ~CACHETHEN;
+
+       ahc_outb(ahc, DSCOMMAND0, dscommand0);
+
+       ahc->pci_cachesize =
+           ahc_pci_read_config(ahc->dev_softc, CSIZE_LATTIME,
+                               /*bytes*/1) & CACHESIZE;
+       ahc->pci_cachesize *= 4;
+
+       if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0
+        && ahc->pci_cachesize == 4) {
+
+               ahc_pci_write_config(ahc->dev_softc, CSIZE_LATTIME,
+                                    0, /*bytes*/1);
+               ahc->pci_cachesize = 0;
+       }
+
+       /*
+        * We cannot perform ULTRA speeds without the presense
+        * of the external precision resistor.
+        */
+       if ((ahc->features & AHC_ULTRA) != 0) {
+               uint32_t devconfig;
+
+               devconfig = ahc_pci_read_config(ahc->dev_softc,
+                                               DEVCONFIG, /*bytes*/4);
+               if ((devconfig & REXTVALID) == 0)
+                       ahc->features &= ~AHC_ULTRA;
+       }
+
+       /* See if we have a SEEPROM and perform auto-term */
+       check_extport(ahc, &sxfrctl1);
+
+       /*
+        * Take the LED out of diagnostic mode
+        */
+       sblkctl = ahc_inb(ahc, SBLKCTL);
+       ahc_outb(ahc, SBLKCTL, (sblkctl & ~(DIAGLEDEN|DIAGLEDON)));
+
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               ahc_outb(ahc, DFF_THRSH, RD_DFTHRSH_MAX|WR_DFTHRSH_MAX);
+       } else {
+               ahc_outb(ahc, DSPCISTATUS, DFTHRSH_100);
+       }
+
+       if (ahc->flags & AHC_USEDEFAULTS) {
+               /*
+                * PCI Adapter default setup
+                * Should only be used if the adapter does not have
+                * a SEEPROM.
+                */
+               /* See if someone else set us up already */
+               if (scsiseq != 0) {
+                       printf("%s: Using left over BIOS settings\n",
+                               ahc_name(ahc));
+                       ahc->flags &= ~AHC_USEDEFAULTS;
+                       ahc->flags |= AHC_BIOS_ENABLED;
+               } else {
+                       /*
+                        * Assume only one connector and always turn
+                        * on termination.
+                        */
+                       our_id = 0x07;
+                       sxfrctl1 = STPWEN;
+               }
+               ahc_outb(ahc, SCSICONF, our_id|ENSPCHK|RESET_SCSI);
+
+               ahc->our_id = our_id;
+       }
+
+       /*
+        * Take a look to see if we have external SRAM.
+        * We currently do not attempt to use SRAM that is
+        * shared among multiple controllers.
+        */
+       ahc_probe_ext_scbram(ahc);
+
+       /*
+        * Record our termination setting for the
+        * generic initialization routine.
+        */
+       if ((sxfrctl1 & STPWEN) != 0)
+               ahc->flags |= AHC_TERM_ENB_A;
+
+       /* Core initialization */
+       error = ahc_init(ahc);
+       if (error != 0)
+               return (error);
+
+       /*
+        * Allow interrupts now that we are completely setup.
+        */
+       error = ahc_pci_map_int(ahc);
+       if (error != 0)
+               return (error);
+
+       ahc_list_lock(&l);
+       /*
+        * Link this softc in with all other ahc instances.
+        */
+       ahc_softc_insert(ahc);
+
+       ahc_lock(ahc, &s);
+       ahc_intr_enable(ahc, TRUE);
+       ahc_unlock(ahc, &s);
+
+       ahc_list_unlock(&l);
+       return (0);
+}
+
+/*
+ * Test for the presense of external sram in an
+ * "unshared" configuration.
+ */
+static int
+ahc_ext_scbram_present(struct ahc_softc *ahc)
+{
+       u_int chip;
+       int ramps;
+       int single_user;
+       uint32_t devconfig;
+
+       chip = ahc->chip & AHC_CHIPID_MASK;
+       devconfig = ahc_pci_read_config(ahc->dev_softc,
+                                       DEVCONFIG, /*bytes*/4);
+       single_user = (devconfig & MPORTMODE) != 0;
+
+       if ((ahc->features & AHC_ULTRA2) != 0)
+               ramps = (ahc_inb(ahc, DSCOMMAND0) & RAMPS) != 0;
+       else if (chip == AHC_AIC7895 || chip == AHC_AIC7895C)
+               /*
+                * External SCBRAM arbitration is flakey
+                * on these chips.  Unfortunately this means
+                * we don't use the extra SCB ram space on the
+                * 3940AUW.
+                */
+               ramps = 0;
+       else if (chip >= AHC_AIC7870)
+               ramps = (devconfig & RAMPSM) != 0;
+       else
+               ramps = 0;
+
+       if (ramps && single_user)
+               return (1);
+       return (0);
+}
+
+/*
+ * Enable external scbram.
+ */
+static void
+ahc_scbram_config(struct ahc_softc *ahc, int enable, int pcheck,
+                 int fast, int large)
+{
+       uint32_t devconfig;
+
+       if (ahc->features & AHC_MULTI_FUNC) {
+               /*
+                * Set the SCB Base addr (highest address bit)
+                * depending on which channel we are.
+                */
+               ahc_outb(ahc, SCBBADDR, ahc_get_pci_function(ahc->dev_softc));
+       }
+
+       ahc->flags &= ~AHC_LSCBS_ENABLED;
+       if (large)
+               ahc->flags |= AHC_LSCBS_ENABLED;
+       devconfig = ahc_pci_read_config(ahc->dev_softc, DEVCONFIG, /*bytes*/4);
+       if ((ahc->features & AHC_ULTRA2) != 0) {
+               u_int dscommand0;
+
+               dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+               if (enable)
+                       dscommand0 &= ~INTSCBRAMSEL;
+               else
+                       dscommand0 |= INTSCBRAMSEL;
+               if (large)
+                       dscommand0 &= ~USCBSIZE32;
+               else
+                       dscommand0 |= USCBSIZE32;
+               ahc_outb(ahc, DSCOMMAND0, dscommand0);
+       } else {
+               if (fast)
+                       devconfig &= ~EXTSCBTIME;
+               else
+                       devconfig |= EXTSCBTIME;
+               if (enable)
+                       devconfig &= ~SCBRAMSEL;
+               else
+                       devconfig |= SCBRAMSEL;
+               if (large)
+                       devconfig &= ~SCBSIZE32;
+               else
+                       devconfig |= SCBSIZE32;
+       }
+       if (pcheck)
+               devconfig |= EXTSCBPEN;
+       else
+               devconfig &= ~EXTSCBPEN;
+
+       ahc_pci_write_config(ahc->dev_softc, DEVCONFIG, devconfig, /*bytes*/4);
+}
+
+/*
+ * Take a look to see if we have external SRAM.
+ * We currently do not attempt to use SRAM that is
+ * shared among multiple controllers.
+ */
+static void
+ahc_probe_ext_scbram(struct ahc_softc *ahc)
+{
+       int num_scbs;
+       int test_num_scbs;
+       int enable;
+       int pcheck;
+       int fast;
+       int large;
+
+       enable = FALSE;
+       pcheck = FALSE;
+       fast = FALSE;
+       large = FALSE;
+       num_scbs = 0;
+       
+       if (ahc_ext_scbram_present(ahc) == 0)
+               goto done;
+
+       /*
+        * Probe for the best parameters to use.
+        */
+       ahc_scbram_config(ahc, /*enable*/TRUE, pcheck, fast, large);
+       num_scbs = ahc_probe_scbs(ahc);
+       if (num_scbs == 0) {
+               /* The SRAM wasn't really present. */
+               goto done;
+       }
+       enable = TRUE;
+
+       /*
+        * Clear any outstanding parity error
+        * and ensure that parity error reporting
+        * is enabled.
+        */
+       ahc_outb(ahc, SEQCTL, 0);
+       ahc_outb(ahc, CLRINT, CLRPARERR);
+       ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+
+       /* Now see if we can do parity */
+       ahc_scbram_config(ahc, enable, /*pcheck*/TRUE, fast, large);
+       num_scbs = ahc_probe_scbs(ahc);
+       if ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0
+        || (ahc_inb(ahc, ERROR) & MPARERR) == 0)
+               pcheck = TRUE;
+
+       /* Clear any resulting parity error */
+       ahc_outb(ahc, CLRINT, CLRPARERR);
+       ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+
+       /* Now see if we can do fast timing */
+       ahc_scbram_config(ahc, enable, pcheck, /*fast*/TRUE, large);
+       test_num_scbs = ahc_probe_scbs(ahc);
+       if (test_num_scbs == num_scbs
+        && ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0
+         || (ahc_inb(ahc, ERROR) & MPARERR) == 0))
+               fast = TRUE;
+
+       /*
+        * See if we can use large SCBs and still maintain
+        * the same overall count of SCBs.
+        */
+       if ((ahc->features & AHC_LARGE_SCBS) != 0) {
+               ahc_scbram_config(ahc, enable, pcheck, fast, /*large*/TRUE);
+               test_num_scbs = ahc_probe_scbs(ahc);
+               if (test_num_scbs >= num_scbs) {
+                       large = TRUE;
+                       num_scbs = test_num_scbs;
+                       if (num_scbs >= 64) {
+                               /*
+                                * We have enough space to move the
+                                * "busy targets table" into SCB space
+                                * and make it qualify all the way to the
+                                * lun level.
+                                */
+                               ahc->flags |= AHC_SCB_BTT;
+                       }
+               }
+       }
+done:
+       /*
+        * Disable parity error reporting until we
+        * can load instruction ram.
+        */
+       ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS);
+       /* Clear any latched parity error */
+       ahc_outb(ahc, CLRINT, CLRPARERR);
+       ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+       if (bootverbose && enable) {
+               printf("%s: External SRAM, %s access%s, %dbytes/SCB\n",
+                      ahc_name(ahc), fast ? "fast" : "slow", 
+                      pcheck ? ", parity checking enabled" : "",
+                      large ? 64 : 32);
+       }
+       ahc_scbram_config(ahc, enable, pcheck, fast, large);
+}
+
+/*
+ * Check the external port logic for a serial eeprom
+ * and termination/cable detection contrls.
+ */
+static void
+check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
+{
+       struct  seeprom_descriptor sd;
+       struct  seeprom_config *sc;
+       int     have_seeprom;
+       int     have_autoterm;
+
+       sd.sd_ahc = ahc;
+       sd.sd_control_offset = SEECTL;          
+       sd.sd_status_offset = SEECTL;           
+       sd.sd_dataout_offset = SEECTL;          
+       sc = ahc->seep_config;
+
+       /*
+        * For some multi-channel devices, the c46 is simply too
+        * small to work.  For the other controller types, we can
+        * get our information from either SEEPROM type.  Set the
+        * type to start our probe with accordingly.
+        */
+       if (ahc->flags & AHC_LARGE_SEEPROM)
+               sd.sd_chip = C56_66;
+       else
+               sd.sd_chip = C46;
+
+       sd.sd_MS = SEEMS;
+       sd.sd_RDY = SEERDY;
+       sd.sd_CS = SEECS;
+       sd.sd_CK = SEECK;
+       sd.sd_DO = SEEDO;
+       sd.sd_DI = SEEDI;
+
+       have_seeprom = ahc_acquire_seeprom(ahc, &sd);
+       if (have_seeprom) {
+
+               if (bootverbose) 
+                       printf("%s: Reading SEEPROM...", ahc_name(ahc));
+
+               for (;;) {
+                       u_int start_addr;
+
+                       start_addr = 32 * (ahc->channel - 'A');
+
+                       have_seeprom = ahc_read_seeprom(&sd, (uint16_t *)sc,
+                                                       start_addr,
+                                                       sizeof(*sc)/2);
+
+                       if (have_seeprom)
+                               have_seeprom = ahc_verify_cksum(sc);
+
+                       if (have_seeprom != 0 || sd.sd_chip == C56_66) {
+                               if (bootverbose) {
+                                       if (have_seeprom == 0)
+                                               printf ("checksum error\n");
+                                       else
+                                               printf ("done.\n");
+                               }
+                               break;
+                       }
+                       sd.sd_chip = C56_66;
+               }
+               ahc_release_seeprom(&sd);
+       }
+
+       if (!have_seeprom) {
+               /*
+                * Pull scratch ram settings and treat them as
+                * if they are the contents of an seeprom if
+                * the 'ADPT' signature is found in SCB2.
+                * We manually compose the data as 16bit values
+                * to avoid endian issues.
+                */
+               ahc_outb(ahc, SCBPTR, 2);
+               if (ahc_inb(ahc, SCB_BASE) == 'A'
+                && ahc_inb(ahc, SCB_BASE + 1) == 'D'
+                && ahc_inb(ahc, SCB_BASE + 2) == 'P'
+                && ahc_inb(ahc, SCB_BASE + 3) == 'T') {
+                       uint16_t *sc_data;
+                       int       i;
+
+                       sc_data = (uint16_t *)sc;
+                       for (i = 0; i < 32; i++) {
+                               uint16_t val;
+                               int      j;
+
+                               j = i * 2;
+                               val = ahc_inb(ahc, SRAM_BASE + j)
+                                   | ahc_inb(ahc, SRAM_BASE + j + 1) << 8;
+                       }
+                       have_seeprom = ahc_verify_cksum(sc);
+                       if (have_seeprom)
+                               ahc->flags |= AHC_SCB_CONFIG_USED;
+               }
+               /*
+                * Clear any SCB parity errors in case this data and
+                * its associated parity was not initialized by the BIOS
+                */
+               ahc_outb(ahc, CLRINT, CLRPARERR);
+               ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+       }
+
+       if (!have_seeprom) {
+               if (bootverbose)
+                       printf("%s: No SEEPROM available.\n", ahc_name(ahc));
+               ahc->flags |= AHC_USEDEFAULTS;
+               free(ahc->seep_config, M_DEVBUF);
+               ahc->seep_config = NULL;
+               sc = NULL;
+       } else {
+               ahc_parse_pci_eeprom(ahc, sc);
+       }
+
+       /*
+        * Cards that have the external logic necessary to talk to
+        * a SEEPROM, are almost certain to have the remaining logic
+        * necessary for auto-termination control.  This assumption
+        * hasn't failed yet...
+        */
+       have_autoterm = have_seeprom;
+
+       /*
+        * Some low-cost chips have SEEPROM and auto-term control built
+        * in, instead of using a GAL.  They can tell us directly
+        * if the termination logic is enabled.
+        */
+       if ((ahc->features & AHC_SPIOCAP) != 0) {
+               if ((ahc_inb(ahc, SPIOCAP) & SSPIOCPS) == 0)
+                       have_autoterm = FALSE;
+       }
+
+       if (have_autoterm) {
+               ahc_acquire_seeprom(ahc, &sd);
+               configure_termination(ahc, &sd, sc->adapter_control, sxfrctl1);
+               ahc_release_seeprom(&sd);
+       } else if (have_seeprom) {
+               *sxfrctl1 &= ~STPWEN;
+               if ((sc->adapter_control & CFSTERM) != 0)
+                       *sxfrctl1 |= STPWEN;
+               if (bootverbose)
+                       printf("%s: Low byte termination %sabled\n",
+                              ahc_name(ahc),
+                              (*sxfrctl1 & STPWEN) ? "en" : "dis");
+       }
+}
+
+static void
+ahc_parse_pci_eeprom(struct ahc_softc *ahc, struct seeprom_config *sc)
+{
+       /*
+        * Put the data we've collected down into SRAM
+        * where ahc_init will find it.
+        */
+       int      i;
+       int      max_targ = sc->max_targets & CFMAXTARG;
+       u_int    scsi_conf;
+       uint16_t discenable;
+       uint16_t ultraenb;
+
+       discenable = 0;
+       ultraenb = 0;
+       if ((sc->adapter_control & CFULTRAEN) != 0) {
+               /*
+                * Determine if this adapter has a "newstyle"
+                * SEEPROM format.
+                */
+               for (i = 0; i < max_targ; i++) {
+                       if ((sc->device_flags[i] & CFSYNCHISULTRA) != 0) {
+                               ahc->flags |= AHC_NEWEEPROM_FMT;
+                               break;
+                       }
+               }
+       }
+
+       for (i = 0; i < max_targ; i++) {
+               u_int     scsirate;
+               uint16_t target_mask;
+
+               target_mask = 0x01 << i;
+               if (sc->device_flags[i] & CFDISC)
+                       discenable |= target_mask;
+               if ((ahc->flags & AHC_NEWEEPROM_FMT) != 0) {
+                       if ((sc->device_flags[i] & CFSYNCHISULTRA) != 0)
+                               ultraenb |= target_mask;
+               } else if ((sc->adapter_control & CFULTRAEN) != 0) {
+                       ultraenb |= target_mask;
+               }
+               if ((sc->device_flags[i] & CFXFER) == 0x04
+                && (ultraenb & target_mask) != 0) {
+                       /* Treat 10MHz as a non-ultra speed */
+                       sc->device_flags[i] &= ~CFXFER;
+                       ultraenb &= ~target_mask;
+               }
+               if ((ahc->features & AHC_ULTRA2) != 0) {
+                       u_int offset;
+
+                       if (sc->device_flags[i] & CFSYNCH)
+                               offset = MAX_OFFSET_ULTRA2;
+                       else 
+                               offset = 0;
+                       ahc_outb(ahc, TARG_OFFSET + i, offset);
+
+                       /*
+                        * The ultra enable bits contain the
+                        * high bit of the ultra2 sync rate
+                        * field.
+                        */
+                       scsirate = (sc->device_flags[i] & CFXFER)
+                                | ((ultraenb & target_mask) ? 0x8 : 0x0);
+                       if (sc->device_flags[i] & CFWIDEB)
+                               scsirate |= WIDEXFER;
+               } else {
+                       scsirate = (sc->device_flags[i] & CFXFER) << 4;
+                       if (sc->device_flags[i] & CFSYNCH)
+                               scsirate |= SOFS;
+                       if (sc->device_flags[i] & CFWIDEB)
+                               scsirate |= WIDEXFER;
+               }
+               ahc_outb(ahc, TARG_SCSIRATE + i, scsirate);
+       }
+       ahc->our_id = sc->brtime_id & CFSCSIID;
+
+       scsi_conf = (ahc->our_id & 0x7);
+       if (sc->adapter_control & CFSPARITY)
+               scsi_conf |= ENSPCHK;
+       if (sc->adapter_control & CFRESETB)
+               scsi_conf |= RESET_SCSI;
+
+       ahc->flags |= (sc->adapter_control & CFBOOTCHAN) >> CFBOOTCHANSHIFT;
+
+       if (sc->bios_control & CFEXTEND)
+               ahc->flags |= AHC_EXTENDED_TRANS_A;
+
+       if (sc->bios_control & CFBIOSEN)
+               ahc->flags |= AHC_BIOS_ENABLED;
+       if (ahc->features & AHC_ULTRA
+        && (ahc->flags & AHC_NEWEEPROM_FMT) == 0) {
+               /* Should we enable Ultra mode? */
+               if (!(sc->adapter_control & CFULTRAEN))
+                       /* Treat us as a non-ultra card */
+                       ultraenb = 0;
+       }
+
+       if (sc->signature == CFSIGNATURE
+        || sc->signature == CFSIGNATURE2) {
+               uint32_t devconfig;
+
+               /* Honor the STPWLEVEL settings */
+               devconfig = ahc_pci_read_config(ahc->dev_softc,
+                                               DEVCONFIG, /*bytes*/4);
+               devconfig &= ~STPWLEVEL;
+               if ((sc->bios_control & CFSTPWLEVEL) != 0)
+                       devconfig |= STPWLEVEL;
+               ahc_pci_write_config(ahc->dev_softc, DEVCONFIG,
+                                    devconfig, /*bytes*/4);
+       }
+       /* Set SCSICONF info */
+       ahc_outb(ahc, SCSICONF, scsi_conf);
+       ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff));
+       ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff));
+       ahc_outb(ahc, ULTRA_ENB, ultraenb & 0xff);
+       ahc_outb(ahc, ULTRA_ENB + 1, (ultraenb >> 8) & 0xff);
+}
+
+static void
+configure_termination(struct ahc_softc *ahc,
+                     struct seeprom_descriptor *sd,
+                     u_int adapter_control,
+                     u_int *sxfrctl1)
+{
+       uint8_t brddat;
+       
+       brddat = 0;
+
+       /*
+        * Update the settings in sxfrctl1 to match the
+        * termination settings 
+        */
+       *sxfrctl1 = 0;
+       
+       /*
+        * SEECS must be on for the GALS to latch
+        * the data properly.  Be sure to leave MS
+        * on or we will release the seeprom.
+        */
+       SEEPROM_OUTB(sd, sd->sd_MS | sd->sd_CS);
+       if ((adapter_control & CFAUTOTERM) != 0
+        || (ahc->features & AHC_NEW_TERMCTL) != 0) {
+               int internal50_present;
+               int internal68_present;
+               int externalcable_present;
+               int eeprom_present;
+               int enableSEC_low;
+               int enableSEC_high;
+               int enablePRI_low;
+               int enablePRI_high;
+               int sum;
+
+               enableSEC_low = 0;
+               enableSEC_high = 0;
+               enablePRI_low = 0;
+               enablePRI_high = 0;
+               if ((ahc->features & AHC_NEW_TERMCTL) != 0) {
+                       ahc_new_term_detect(ahc, &enableSEC_low,
+                                           &enableSEC_high,
+                                           &enablePRI_low,
+                                           &enablePRI_high,
+                                           &eeprom_present);
+                       if ((adapter_control & CFSEAUTOTERM) == 0) {
+                               if (bootverbose)
+                                       printf("%s: Manual SE Termination\n",
+                                              ahc_name(ahc));
+                               enableSEC_low = (adapter_control & CFSELOWTERM);
+                               enableSEC_high =
+                                   (adapter_control & CFSEHIGHTERM);
+                       }
+                       if ((adapter_control & CFAUTOTERM) == 0) {
+                               if (bootverbose)
+                                       printf("%s: Manual LVD Termination\n",
+                                              ahc_name(ahc));
+                               enablePRI_low = (adapter_control & CFSTERM);
+                               enablePRI_high = (adapter_control & CFWSTERM);
+                       }
+                       /* Make the table calculations below happy */
+                       internal50_present = 0;
+                       internal68_present = 1;
+                       externalcable_present = 1;
+               } else if ((ahc->features & AHC_SPIOCAP) != 0) {
+                       aic785X_cable_detect(ahc, &internal50_present,
+                                            &externalcable_present,
+                                            &eeprom_present);
+               } else {
+                       aic787X_cable_detect(ahc, &internal50_present,
+                                            &internal68_present,
+                                            &externalcable_present,
+                                            &eeprom_present);
+               }
+
+               if ((ahc->features & AHC_WIDE) == 0)
+                       internal68_present = 0;
+
+               if (bootverbose
+                && (ahc->features & AHC_ULTRA2) == 0) {
+                       printf("%s: internal 50 cable %s present",
+                              ahc_name(ahc),
+                              internal50_present ? "is":"not");
+
+                       if ((ahc->features & AHC_WIDE) != 0)
+                               printf(", internal 68 cable %s present",
+                                      internal68_present ? "is":"not");
+                       printf("\n%s: external cable %s present\n",
+                              ahc_name(ahc),
+                              externalcable_present ? "is":"not");
+               }
+               if (bootverbose)
+                       printf("%s: BIOS eeprom %s present\n",
+                              ahc_name(ahc), eeprom_present ? "is" : "not");
+
+               if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0) {
+                       /*
+                        * The 50 pin connector is a separate bus,
+                        * so force it to always be terminated.
+                        * In the future, perform current sensing
+                        * to determine if we are in the middle of
+                        * a properly terminated bus.
+                        */
+                       internal50_present = 0;
+               }
+
+               /*
+                * Now set the termination based on what
+                * we found.
+                * Flash Enable = BRDDAT7
+                * Secondary High Term Enable = BRDDAT6
+                * Secondary Low Term Enable = BRDDAT5 (7890)
+                * Primary High Term Enable = BRDDAT4 (7890)
+                */
+               if ((ahc->features & AHC_ULTRA2) == 0
+                && (internal50_present != 0)
+                && (internal68_present != 0)
+                && (externalcable_present != 0)) {
+                       printf("%s: Illegal cable configuration!!. "
+                              "Only two connectors on the "
+                              "adapter may be used at a "
+                              "time!\n", ahc_name(ahc));
+
+                       /*
+                        * Pretend there are no cables in the hope
+                        * that having all of the termination on
+                        * gives us a more stable bus.
+                        */
+                       internal50_present = 0;
+                       internal68_present = 0;
+                       externalcable_present = 0;
+               }
+
+               if ((ahc->features & AHC_WIDE) != 0
+                && ((externalcable_present == 0)
+                 || (internal68_present == 0)
+                 || (enableSEC_high != 0))) {
+                       brddat |= BRDDAT6;
+                       if (bootverbose) {
+                               if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0)
+                                       printf("%s: 68 pin termination "
+                                              "Enabled\n", ahc_name(ahc));
+                               else
+                                       printf("%s: %sHigh byte termination "
+                                              "Enabled\n", ahc_name(ahc),
+                                              enableSEC_high ? "Secondary "
+                                                             : "");
+                       }
+               }
+
+               sum = internal50_present + internal68_present
+                   + externalcable_present;
+               if (sum < 2 || (enableSEC_low != 0)) {
+                       if ((ahc->features & AHC_ULTRA2) != 0)
+                               brddat |= BRDDAT5;
+                       else
+                               *sxfrctl1 |= STPWEN;
+                       if (bootverbose) {
+                               if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0)
+                                       printf("%s: 50 pin termination "
+                                              "Enabled\n", ahc_name(ahc));
+                               else
+                                       printf("%s: %sLow byte termination "
+                                              "Enabled\n", ahc_name(ahc),
+                                              enableSEC_low ? "Secondary "
+                                                            : "");
+                       }
+               }
+
+               if (enablePRI_low != 0) {
+                       *sxfrctl1 |= STPWEN;
+                       if (bootverbose)
+                               printf("%s: Primary Low Byte termination "
+                                      "Enabled\n", ahc_name(ahc));
+               }
+
+               /*
+                * Setup STPWEN before setting up the rest of
+                * the termination per the tech note on the U160 cards.
+                */
+               ahc_outb(ahc, SXFRCTL1, *sxfrctl1);
+
+               if (enablePRI_high != 0) {
+                       brddat |= BRDDAT4;
+                       if (bootverbose)
+                               printf("%s: Primary High Byte "
+                                      "termination Enabled\n",
+                                      ahc_name(ahc));
+               }
+               
+               write_brdctl(ahc, brddat);
+
+       } else {
+               if ((adapter_control & CFSTERM) != 0) {
+                       *sxfrctl1 |= STPWEN;
+
+                       if (bootverbose)
+                               printf("%s: %sLow byte termination Enabled\n",
+                                      ahc_name(ahc),
+                                      (ahc->features & AHC_ULTRA2) ? "Primary "
+                                                                   : "");
+               }
+
+               if ((adapter_control & CFWSTERM) != 0
+                && (ahc->features & AHC_WIDE) != 0) {
+                       brddat |= BRDDAT6;
+                       if (bootverbose)
+                               printf("%s: %sHigh byte termination Enabled\n",
+                                      ahc_name(ahc),
+                                      (ahc->features & AHC_ULTRA2)
+                                    ? "Secondary " : "");
+               }
+
+               /*
+                * Setup STPWEN before setting up the rest of
+                * the termination per the tech note on the U160 cards.
+                */
+               ahc_outb(ahc, SXFRCTL1, *sxfrctl1);
+
+               if ((ahc->features & AHC_WIDE) != 0)
+                       write_brdctl(ahc, brddat);
+       }
+       SEEPROM_OUTB(sd, sd->sd_MS); /* Clear CS */
+}
+
+static void
+ahc_new_term_detect(struct ahc_softc *ahc, int *enableSEC_low,
+                   int *enableSEC_high, int *enablePRI_low,
+                   int *enablePRI_high, int *eeprom_present)
+{
+       uint8_t brdctl;
+
+       /*
+        * BRDDAT7 = Eeprom
+        * BRDDAT6 = Enable Secondary High Byte termination
+        * BRDDAT5 = Enable Secondary Low Byte termination
+        * BRDDAT4 = Enable Primary high byte termination
+        * BRDDAT3 = Enable Primary low byte termination
+        */
+       brdctl = read_brdctl(ahc);
+       *eeprom_present = brdctl & BRDDAT7;
+       *enableSEC_high = (brdctl & BRDDAT6);
+       *enableSEC_low = (brdctl & BRDDAT5);
+       *enablePRI_high = (brdctl & BRDDAT4);
+       *enablePRI_low = (brdctl & BRDDAT3);
+}
+
+static void
+aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+                    int *internal68_present, int *externalcable_present,
+                    int *eeprom_present)
+{
+       uint8_t brdctl;
+
+       /*
+        * First read the status of our cables.
+        * Set the rom bank to 0 since the
+        * bank setting serves as a multiplexor
+        * for the cable detection logic.
+        * BRDDAT5 controls the bank switch.
+        */
+       write_brdctl(ahc, 0);
+
+       /*
+        * Now read the state of the internal
+        * connectors.  BRDDAT6 is INT50 and
+        * BRDDAT7 is INT68.
+        */
+       brdctl = read_brdctl(ahc);
+       *internal50_present = (brdctl & BRDDAT6) ? 0 : 1;
+       *internal68_present = (brdctl & BRDDAT7) ? 0 : 1;
+
+       /*
+        * Set the rom bank to 1 and determine
+        * the other signals.
+        */
+       write_brdctl(ahc, BRDDAT5);
+
+       /*
+        * Now read the state of the external
+        * connectors.  BRDDAT6 is EXT68 and
+        * BRDDAT7 is EPROMPS.
+        */
+       brdctl = read_brdctl(ahc);
+       *externalcable_present = (brdctl & BRDDAT6) ? 0 : 1;
+       *eeprom_present = (brdctl & BRDDAT7) ? 1 : 0;
+}
+
+static void
+aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+                    int *externalcable_present, int *eeprom_present)
+{
+       uint8_t brdctl;
+       uint8_t spiocap;
+
+       spiocap = ahc_inb(ahc, SPIOCAP);
+       spiocap &= ~SOFTCMDEN;
+       spiocap |= EXT_BRDCTL;
+       ahc_outb(ahc, SPIOCAP, spiocap);
+       ahc_outb(ahc, BRDCTL, BRDRW|BRDCS);
+       ahc_outb(ahc, BRDCTL, 0);
+       brdctl = ahc_inb(ahc, BRDCTL);
+       *internal50_present = (brdctl & BRDDAT5) ? 0 : 1;
+       *externalcable_present = (brdctl & BRDDAT6) ? 0 : 1;
+
+       *eeprom_present = (ahc_inb(ahc, SPIOCAP) & EEPROM) ? 1 : 0;
+}
+       
+int
+ahc_acquire_seeprom(struct ahc_softc *ahc, struct seeprom_descriptor *sd)
+{
+       int wait;
+
+       if ((ahc->features & AHC_SPIOCAP) != 0
+        && (ahc_inb(ahc, SPIOCAP) & SEEPROM) == 0)
+               return (0);
+
+       /*
+        * Request access of the memory port.  When access is
+        * granted, SEERDY will go high.  We use a 1 second
+        * timeout which should be near 1 second more than
+        * is needed.  Reason: after the chip reset, there
+        * should be no contention.
+        */
+       SEEPROM_OUTB(sd, sd->sd_MS);
+       wait = 1000;  /* 1 second timeout in msec */
+       while (--wait && ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0)) {
+               ahc_delay(1000);  /* delay 1 msec */
+       }
+       if ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0) {
+               SEEPROM_OUTB(sd, 0); 
+               return (0);
+       }
+       return(1);
+}
+
+void
+ahc_release_seeprom(struct seeprom_descriptor *sd)
+{
+       /* Release access to the memory port and the serial EEPROM. */
+       SEEPROM_OUTB(sd, 0);
+}
+
+static void
+write_brdctl(struct ahc_softc *ahc, uint8_t value)
+{
+       uint8_t brdctl;
+
+       if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+               brdctl = BRDSTB;
+               if (ahc->channel == 'B')
+                       brdctl |= BRDCS;
+       } else if ((ahc->features & AHC_ULTRA2) != 0) {
+               brdctl = 0;
+       } else {
+               brdctl = BRDSTB|BRDCS;
+       }
+       ahc_outb(ahc, BRDCTL, brdctl);
+       ahc_flush_device_writes(ahc);
+       brdctl |= value;
+       ahc_outb(ahc, BRDCTL, brdctl);
+       ahc_flush_device_writes(ahc);
+       if ((ahc->features & AHC_ULTRA2) != 0)
+               brdctl |= BRDSTB_ULTRA2;
+       else
+               brdctl &= ~BRDSTB;
+       ahc_outb(ahc, BRDCTL, brdctl);
+       ahc_flush_device_writes(ahc);
+       if ((ahc->features & AHC_ULTRA2) != 0)
+               brdctl = 0;
+       else
+               brdctl &= ~BRDCS;
+       ahc_outb(ahc, BRDCTL, brdctl);
+}
+
+static uint8_t
+read_brdctl(ahc)
+       struct  ahc_softc *ahc;
+{
+       uint8_t brdctl;
+       uint8_t value;
+
+       if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+               brdctl = BRDRW;
+               if (ahc->channel == 'B')
+                       brdctl |= BRDCS;
+       } else if ((ahc->features & AHC_ULTRA2) != 0) {
+               brdctl = BRDRW_ULTRA2;
+       } else {
+               brdctl = BRDRW|BRDCS;
+       }
+       ahc_outb(ahc, BRDCTL, brdctl);
+       ahc_flush_device_writes(ahc);
+       value = ahc_inb(ahc, BRDCTL);
+       ahc_outb(ahc, BRDCTL, 0);
+       return (value);
+}
+
+#define        DPE     0x80
+#define SSE    0x40
+#define        RMA     0x20
+#define        RTA     0x10
+#define STA    0x08
+#define DPR    0x01
+
+void
+ahc_pci_intr(struct ahc_softc *ahc)
+{
+       u_int error;
+       u_int status1;
+
+       error = ahc_inb(ahc, ERROR);
+       if ((error & PCIERRSTAT) == 0)
+               return;
+
+       status1 = ahc_pci_read_config(ahc->dev_softc,
+                                     PCIR_STATUS + 1, /*bytes*/1);
+
+       printf("%s: PCI error Interrupt at seqaddr = 0x%x\n",
+             ahc_name(ahc),
+             ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+
+       if (status1 & DPE) {
+               printf("%s: Data Parity Error Detected during address "
+                      "or write data phase\n", ahc_name(ahc));
+       }
+       if (status1 & SSE) {
+               printf("%s: Signal System Error Detected\n", ahc_name(ahc));
+       }
+       if (status1 & RMA) {
+               printf("%s: Received a Master Abort\n", ahc_name(ahc));
+       }
+       if (status1 & RTA) {
+               printf("%s: Received a Target Abort\n", ahc_name(ahc));
+       }
+       if (status1 & STA) {
+               printf("%s: Signaled a Target Abort\n", ahc_name(ahc));
+       }
+       if (status1 & DPR) {
+               printf("%s: Data Parity Error has been reported via PERR#\n",
+                      ahc_name(ahc));
+       }
+
+       /* Clear latched errors. */
+       ahc_pci_write_config(ahc->dev_softc, PCIR_STATUS + 1,
+                            status1, /*bytes*/1);
+
+       if ((status1 & (DPE|SSE|RMA|RTA|STA|DPR)) == 0) {
+               printf("%s: Latched PCIERR interrupt with "
+                      "no status bits set\n", ahc_name(ahc)); 
+       } else {
+               ahc_outb(ahc, CLRINT, CLRPARERR);
+       }
+
+       ahc_unpause(ahc);
+}
+
+static int
+ahc_aic785X_setup(struct ahc_softc *ahc)
+{
+       ahc_dev_softc_t pci;
+       uint8_t rev;
+
+       pci = ahc->dev_softc;
+       ahc->channel = 'A';
+       ahc->chip = AHC_AIC7850;
+       ahc->features = AHC_AIC7850_FE;
+       ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+       rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+       if (rev >= 1)
+               ahc->bugs |= AHC_PCI_2_1_RETRY_BUG;
+       return (0);
+}
+
+static int
+ahc_aic7860_setup(struct ahc_softc *ahc)
+{
+       ahc_dev_softc_t pci;
+       uint8_t rev;
+
+       pci = ahc->dev_softc;
+       ahc->channel = 'A';
+       ahc->chip = AHC_AIC7860;
+       ahc->features = AHC_AIC7860_FE;
+       ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+       rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+       if (rev >= 1)
+               ahc->bugs |= AHC_PCI_2_1_RETRY_BUG;
+       return (0);
+}
+
+static int
+ahc_apa1480_setup(struct ahc_softc *ahc)
+{
+       ahc_dev_softc_t pci;
+       int error;
+
+       pci = ahc->dev_softc;
+       error = ahc_aic7860_setup(ahc);
+       if (error != 0)
+               return (error);
+       ahc->features |= AHC_REMOVABLE;
+       return (0);
+}
+
+static int
+ahc_aic7870_setup(struct ahc_softc *ahc)
+{
+       ahc_dev_softc_t pci;
+
+       pci = ahc->dev_softc;
+       ahc->channel = 'A';
+       ahc->chip = AHC_AIC7870;
+       ahc->features = AHC_AIC7870_FE;
+       ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+       return (0);
+}
+
+static int
+ahc_aha394X_setup(struct ahc_softc *ahc)
+{
+       int error;
+
+       error = ahc_aic7870_setup(ahc);
+       if (error == 0)
+               error = ahc_aha394XX_setup(ahc);
+       return (error);
+}
+
+static int
+ahc_aha398X_setup(struct ahc_softc *ahc)
+{
+       int error;
+
+       error = ahc_aic7870_setup(ahc);
+       if (error == 0)
+               error = ahc_aha398XX_setup(ahc);
+       return (error);
+}
+
+static int
+ahc_aha494X_setup(struct ahc_softc *ahc)
+{
+       int error;
+
+       error = ahc_aic7870_setup(ahc);
+       if (error == 0)
+               error = ahc_aha494XX_setup(ahc);
+       return (error);
+}
+
+static int
+ahc_aic7880_setup(struct ahc_softc *ahc)
+{
+       ahc_dev_softc_t pci;
+       uint8_t rev;
+
+       pci = ahc->dev_softc;
+       ahc->channel = 'A';
+       ahc->chip = AHC_AIC7880;
+       ahc->features = AHC_AIC7880_FE;
+       ahc->bugs |= AHC_TMODE_WIDEODD_BUG;
+       rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+       if (rev >= 1) {
+               ahc->bugs |= AHC_PCI_2_1_RETRY_BUG;
+       } else {
+               ahc->bugs |= AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+       }
+       return (0);
+}
+
+static int
+ahc_aha2940Pro_setup(struct ahc_softc *ahc)
+{
+       ahc_dev_softc_t pci;
+       int error;
+
+       pci = ahc->dev_softc;
+       ahc->flags |= AHC_INT50_SPEEDFLEX;
+       error = ahc_aic7880_setup(ahc);
+       return (0);
+}
+
+static int
+ahc_aha394XU_setup(struct ahc_softc *ahc)
+{
+       int error;
+
+       error = ahc_aic7880_setup(ahc);
+       if (error == 0)
+               error = ahc_aha394XX_setup(ahc);
+       return (error);
+}
+
+static int
+ahc_aha398XU_setup(struct ahc_softc *ahc)
+{
+       int error;
+
+       error = ahc_aic7880_setup(ahc);
+       if (error == 0)
+               error = ahc_aha398XX_setup(ahc);
+       return (error);
+}
+
+static int
+ahc_aic7890_setup(struct ahc_softc *ahc)
+{
+       ahc_dev_softc_t pci;
+       uint8_t rev;
+
+       pci = ahc->dev_softc;
+       ahc->channel = 'A';
+       ahc->chip = AHC_AIC7890;
+       ahc->features = AHC_AIC7890_FE;
+       ahc->flags |= AHC_NEWEEPROM_FMT;
+       rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+       if (rev == 0)
+               ahc->bugs |= AHC_AUTOFLUSH_BUG|AHC_CACHETHEN_BUG;
+       return (0);
+}
+
+static int
+ahc_aic7892_setup(struct ahc_softc *ahc)
+{
+       ahc_dev_softc_t pci;
+
+       pci = ahc->dev_softc;
+       ahc->channel = 'A';
+       ahc->chip = AHC_AIC7892;
+       ahc->features = AHC_AIC7892_FE;
+       ahc->flags |= AHC_NEWEEPROM_FMT;
+       ahc->bugs |= AHC_SCBCHAN_UPLOAD_BUG;
+       return (0);
+}
+
+static int
+ahc_aic7895_setup(struct ahc_softc *ahc)
+{
+       ahc_dev_softc_t pci;
+       uint8_t rev;
+
+       pci = ahc->dev_softc;
+       ahc->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+       /*
+        * The 'C' revision of the aic7895 has a few additional features.
+        */
+       rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+       if (rev >= 4) {
+               ahc->chip = AHC_AIC7895C;
+               ahc->features = AHC_AIC7895C_FE;
+       } else  {
+               u_int command;
+
+               ahc->chip = AHC_AIC7895;
+               ahc->features = AHC_AIC7895_FE;
+
+               /*
+                * The BIOS disables the use of MWI transactions
+                * since it does not have the MWI bug work around
+                * we have.  Disabling MWI reduces performance, so
+                * turn it on again.
+                */
+               command = ahc_pci_read_config(pci, PCIR_COMMAND, /*bytes*/1);
+               command |= PCIM_CMD_MWRICEN;
+               ahc_pci_write_config(pci, PCIR_COMMAND, command, /*bytes*/1);
+               ahc->bugs |= AHC_PCI_MWI_BUG;
+       }
+       /*
+        * XXX Does CACHETHEN really not work???  What about PCI retry?
+        * on C level chips.  Need to test, but for now, play it safe.
+        */
+       ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_PCI_2_1_RETRY_BUG
+                 |  AHC_CACHETHEN_BUG;
+
+#if 0
+       uint32_t devconfig;
+
+       /*
+        * Cachesize must also be zero due to stray DAC
+        * problem when sitting behind some bridges.
+        */
+       ahc_pci_write_config(pci, CSIZE_LATTIME, 0, /*bytes*/1);
+       devconfig = ahc_pci_read_config(pci, DEVCONFIG, /*bytes*/1);
+       devconfig |= MRDCEN;
+       ahc_pci_write_config(pci, DEVCONFIG, devconfig, /*bytes*/1);
+#endif
+       ahc->flags |= AHC_NEWEEPROM_FMT;
+       return (0);
+}
+
+static int
+ahc_aic7896_setup(struct ahc_softc *ahc)
+{
+       ahc_dev_softc_t pci;
+
+       pci = ahc->dev_softc;
+       ahc->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+       ahc->chip = AHC_AIC7896;
+       ahc->features = AHC_AIC7896_FE;
+       ahc->flags |= AHC_NEWEEPROM_FMT;
+       ahc->bugs |= AHC_CACHETHEN_DIS_BUG;
+       return (0);
+}
+
+static int
+ahc_aic7899_setup(struct ahc_softc *ahc)
+{
+       ahc_dev_softc_t pci;
+
+       pci = ahc->dev_softc;
+       ahc->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+       ahc->chip = AHC_AIC7899;
+       ahc->features = AHC_AIC7899_FE;
+       ahc->flags |= AHC_NEWEEPROM_FMT;
+       ahc->bugs |= AHC_SCBCHAN_UPLOAD_BUG;
+       return (0);
+}
+
+static int
+ahc_aha29160C_setup(struct ahc_softc *ahc)
+{
+       int error;
+
+       error = ahc_aic7899_setup(ahc);
+       if (error != 0)
+               return (error);
+       ahc->features |= AHC_REMOVABLE;
+       return (0);
+}
+
+static int
+ahc_raid_setup(struct ahc_softc *ahc)
+{
+       printf("RAID functionality unsupported\n");
+       return (ENXIO);
+}
+
+static int
+ahc_aha394XX_setup(struct ahc_softc *ahc)
+{
+       ahc_dev_softc_t pci;
+
+       pci = ahc->dev_softc;
+       switch (ahc_get_pci_slot(pci)) {
+       case AHC_394X_SLOT_CHANNEL_A:
+               ahc->channel = 'A';
+               break;
+       case AHC_394X_SLOT_CHANNEL_B:
+               ahc->channel = 'B';
+               break;
+       default:
+               printf("adapter at unexpected slot %d\n"
+                      "unable to map to a channel\n",
+                      ahc_get_pci_slot(pci));
+               ahc->channel = 'A';
+       }
+       return (0);
+}
+
+static int
+ahc_aha398XX_setup(struct ahc_softc *ahc)
+{
+       ahc_dev_softc_t pci;
+
+       pci = ahc->dev_softc;
+       switch (ahc_get_pci_slot(pci)) {
+       case AHC_398X_SLOT_CHANNEL_A:
+               ahc->channel = 'A';
+               break;
+       case AHC_398X_SLOT_CHANNEL_B:
+               ahc->channel = 'B';
+               break;
+       case AHC_398X_SLOT_CHANNEL_C:
+               ahc->channel = 'C';
+               break;
+       default:
+               printf("adapter at unexpected slot %d\n"
+                      "unable to map to a channel\n",
+                      ahc_get_pci_slot(pci));
+               ahc->channel = 'A';
+               break;
+       }
+       ahc->flags |= AHC_LARGE_SEEPROM;
+       return (0);
+}
+
+static int
+ahc_aha494XX_setup(struct ahc_softc *ahc)
+{
+       ahc_dev_softc_t pci;
+
+       pci = ahc->dev_softc;
+       switch (ahc_get_pci_slot(pci)) {
+       case AHC_494X_SLOT_CHANNEL_A:
+               ahc->channel = 'A';
+               break;
+       case AHC_494X_SLOT_CHANNEL_B:
+               ahc->channel = 'B';
+               break;
+       case AHC_494X_SLOT_CHANNEL_C:
+               ahc->channel = 'C';
+               break;
+       case AHC_494X_SLOT_CHANNEL_D:
+               ahc->channel = 'D';
+               break;
+       default:
+               printf("adapter at unexpected slot %d\n"
+                      "unable to map to a channel\n",
+                      ahc_get_pci_slot(pci));
+               ahc->channel = 'A';
+       }
+       ahc->flags |= AHC_LARGE_SEEPROM;
+       return (0);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_proc.c b/xen/drivers/scsi/aic7xxx/aic7xxx_proc.c
new file mode 100644 (file)
index 0000000..9c93f57
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * String handling code courtesy of Gerard Roudier's <groudier@club-internet.fr>
+ * sym driver.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_proc.c#19 $
+ */
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+
+static void    copy_mem_info(struct info_str *info, char *data, int len);
+static int     copy_info(struct info_str *info, char *fmt, ...);
+static u_int   scsi_calc_syncsrate(u_int period_factor);
+static void    ahc_dump_target_state(struct ahc_softc *ahc,
+                                     struct info_str *info,
+                                     u_int our_id, char channel,
+                                     u_int target_id, u_int target_offset);
+static void    ahc_dump_device_state(struct info_str *info,
+                                     struct ahc_linux_device *dev);
+static int     ahc_proc_write_seeprom(struct ahc_softc *ahc,
+                                      char *buffer, int length);
+
+static void
+copy_mem_info(struct info_str *info, char *data, int len)
+{
+       if (info->pos + len > info->offset + info->length)
+               len = info->offset + info->length - info->pos;
+
+       if (info->pos + len < info->offset) {
+               info->pos += len;
+               return;
+       }
+
+       if (info->pos < info->offset) {
+               off_t partial;
+
+               partial = info->offset - info->pos;
+               data += partial;
+               info->pos += partial;
+               len  -= partial;
+       }
+
+       if (len > 0) {
+               memcpy(info->buffer, data, len);
+               info->pos += len;
+               info->buffer += len;
+       }
+}
+
+static int
+copy_info(struct info_str *info, char *fmt, ...)
+{
+       va_list args;
+       char buf[256];
+       int len;
+
+       va_start(args, fmt);
+       len = vsprintf(buf, fmt, args);
+       va_end(args);
+
+       copy_mem_info(info, buf, len);
+       return (len);
+}
+
+/*
+ * Table of syncrates that don't follow the "divisible by 4"
+ * rule. This table will be expanded in future SCSI specs.
+ */
+static struct {
+       u_int period_factor;
+       u_int period;   /* in 10ths of ns */
+} scsi_syncrates[] = {
+       { 0x09, 125 },  /* FAST-80 */
+       { 0x0a, 250 },  /* FAST-40 40MHz */
+       { 0x0b, 303 },  /* FAST-40 33MHz */
+       { 0x0c, 500 }   /* FAST-20 */
+};
+/*
+ * Return the frequency in kHz corresponding to the given
+ * sync period factor.
+ */
+static u_int
+scsi_calc_syncsrate(u_int period_factor)
+{
+       int i; 
+       int num_syncrates;
+       num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]);
+       /* See if the period is in the "exception" table */
+       for (i = 0; i < num_syncrates; i++) {
+
+               if (period_factor == scsi_syncrates[i].period_factor) {
+                               /* Period in kHz */
+                       return (10000000 / scsi_syncrates[i].period);
+               }
+       }
+
+       /*
+        * Wasn't in the table, so use the standard
+        * 4 times conversion.
+        */
+       return (10000000 / (period_factor * 4 * 10));
+}
+
+void
+ahc_format_transinfo(struct info_str *info, struct ahc_transinfo *tinfo)
+{
+       u_int speed;
+       u_int freq;
+       u_int mb;
+
+        speed = 3300;
+        freq = 0;
+       if (tinfo->offset != 0) {
+               freq = scsi_calc_syncsrate(tinfo->period);
+               speed = freq;
+       }
+       speed *= (0x01 << tinfo->width);
+        mb = speed / 1000;
+        if (mb > 0)
+               copy_info(info, "%d.%03dMB/s transfers", mb, speed % 1000);
+        else
+               copy_info(info, "%dKB/s transfers", speed);
+
+       if (freq != 0) {
+               copy_info(info, " (%d.%03dMHz%s, offset %d",
+                        freq / 1000, freq % 1000,
+                        (tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0
+                        ? " DT" : "", tinfo->offset);
+       }
+
+       if (tinfo->width > 0) {
+               if (freq != 0) {
+                       copy_info(info, ", ");
+               } else {
+                       copy_info(info, " (");
+               }
+               copy_info(info, "%dbit)", 8 * (0x01 << tinfo->width));
+       } else if (freq != 0) {
+               copy_info(info, ")");
+       }
+       copy_info(info, "\n");
+}
+
+static void
+ahc_dump_target_state(struct ahc_softc *ahc, struct info_str *info,
+                     u_int our_id, char channel, u_int target_id,
+                     u_int target_offset)
+{
+       struct  ahc_linux_target *targ;
+       struct  ahc_initiator_tinfo *tinfo;
+       struct  ahc_tmode_tstate *tstate;
+       int     lun;
+
+       tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+                                   target_id, &tstate);
+       copy_info(info, "Channel %c Target %d Negotiation Settings\n",
+                 channel, target_id);
+       copy_info(info, "\tUser: ");
+       ahc_format_transinfo(info, &tinfo->user);
+       targ = ahc->platform_data->targets[target_offset];
+       if (targ == NULL)
+               return;
+
+       copy_info(info, "\tGoal: ");
+       ahc_format_transinfo(info, &tinfo->goal);
+       copy_info(info, "\tCurr: ");
+       ahc_format_transinfo(info, &tinfo->curr);
+
+       for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+               struct ahc_linux_device *dev;
+
+               dev = targ->devices[lun];
+
+               if (dev == NULL)
+                       continue;
+
+               ahc_dump_device_state(info, dev);
+       }
+}
+
+static void
+ahc_dump_device_state(struct info_str *info, struct ahc_linux_device *dev)
+{
+       copy_info(info, "\tChannel %c Target %d Lun %d Settings\n",
+                 dev->target->channel + 'A', dev->target->target, dev->lun);
+
+       copy_info(info, "\t\tCommands Queued %ld\n", dev->commands_issued);
+       copy_info(info, "\t\tCommands Active %d\n", dev->active);
+       copy_info(info, "\t\tCommand Openings %d\n", dev->openings);
+       copy_info(info, "\t\tMax Tagged Openings %d\n", dev->maxtags);
+       copy_info(info, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
+}
+
+static int
+ahc_proc_write_seeprom(struct ahc_softc *ahc, char *buffer, int length)
+{
+       struct seeprom_descriptor sd;
+       int have_seeprom;
+       u_long s;
+       int paused;
+       int written;
+
+       /* Default to failure. */
+       written = -EINVAL;
+       ahc_lock(ahc, &s);
+       paused = ahc_is_paused(ahc);
+       if (!paused)
+               ahc_pause(ahc);
+
+       if (length != sizeof(struct seeprom_config)) {
+               printf("ahc_proc_write_seeprom: incorrect buffer size\n");
+               goto done;
+       }
+
+       have_seeprom = ahc_verify_cksum((struct seeprom_config*)buffer);
+       if (have_seeprom == 0) {
+               printf("ahc_proc_write_seeprom: cksum verification failed\n");
+               goto done;
+       }
+
+       sd.sd_ahc = ahc;
+       if ((ahc->chip & AHC_VL) != 0) {
+               sd.sd_control_offset = SEECTL_2840;
+               sd.sd_status_offset = STATUS_2840;
+               sd.sd_dataout_offset = STATUS_2840;             
+               sd.sd_chip = C46;
+               sd.sd_MS = 0;
+               sd.sd_RDY = EEPROM_TF;
+               sd.sd_CS = CS_2840;
+               sd.sd_CK = CK_2840;
+               sd.sd_DO = DO_2840;
+               sd.sd_DI = DI_2840;
+               have_seeprom = TRUE;
+       } else {
+               sd.sd_control_offset = SEECTL;
+               sd.sd_status_offset = SEECTL;
+               sd.sd_dataout_offset = SEECTL;
+               if (ahc->flags & AHC_LARGE_SEEPROM)
+                       sd.sd_chip = C56_66;
+               else
+                       sd.sd_chip = C46;
+               sd.sd_MS = SEEMS;
+               sd.sd_RDY = SEERDY;
+               sd.sd_CS = SEECS;
+               sd.sd_CK = SEECK;
+               sd.sd_DO = SEEDO;
+               sd.sd_DI = SEEDI;
+               have_seeprom = ahc_acquire_seeprom(ahc, &sd);
+       }
+
+       if (!have_seeprom) {
+               printf("ahc_proc_write_seeprom: No Serial EEPROM\n");
+               goto done;
+       } else {
+               u_int start_addr;
+
+               if (ahc->seep_config == NULL) {
+                       ahc->seep_config = malloc(sizeof(*ahc->seep_config),
+                                                 M_DEVBUF, M_NOWAIT);
+                       if (ahc->seep_config == NULL) {
+                               printf("aic7xxx: Unable to allocate serial "
+                                      "eeprom buffer.  Write failing\n");
+                               goto done;
+                       }
+               }
+               printf("aic7xxx: Writing Serial EEPROM\n");
+               start_addr = 32 * (ahc->channel - 'A');
+               ahc_write_seeprom(&sd, (u_int16_t *)buffer, start_addr,
+                                 sizeof(struct seeprom_config)/2);
+               ahc_read_seeprom(&sd, (uint16_t *)ahc->seep_config,
+                                start_addr, sizeof(struct seeprom_config)/2);
+               if ((ahc->chip & AHC_VL) == 0)
+                       ahc_release_seeprom(&sd);
+               written = length;
+       }
+
+done:
+       if (!paused)
+               ahc_unpause(ahc);
+       ahc_unlock(ahc, &s);
+       return (written);
+}
+
+/*
+ * Return information to handle /proc support for the driver.
+ */
+int
+ahc_linux_proc_info(char *buffer, char **start, off_t offset,
+                 int length, int hostno, int inout)
+{
+       struct  ahc_softc *ahc;
+       struct  info_str info;
+       char    ahc_info[256];
+       u_long  s;
+       u_int   max_targ;
+       u_int   i;
+       int     retval;
+
+       retval = -EINVAL;
+       ahc_list_lock(&s);
+       TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+               if (ahc->platform_data->host->host_no == hostno)
+                       break;
+       }
+
+       if (ahc == NULL)
+               goto done;
+
+        /* Has data been written to the file? */ 
+       if (inout == TRUE) {
+               retval = ahc_proc_write_seeprom(ahc, buffer, length);
+               goto done;
+       }
+
+       if (start)
+               *start = buffer;
+
+       info.buffer     = buffer;
+       info.length     = length;
+       info.offset     = offset;
+       info.pos        = 0;
+
+       copy_info(&info, "Adaptec AIC7xxx driver version: %s\n",
+                 AIC7XXX_DRIVER_VERSION);
+       ahc_controller_info(ahc, ahc_info);
+       copy_info(&info, "%s\n\n", ahc_info);
+
+       if (ahc->seep_config == NULL)
+               copy_info(&info, "No Serial EEPROM\n");
+       else {
+               copy_info(&info, "Serial EEPROM:\n");
+               for (i = 0; i < sizeof(*ahc->seep_config)/2; i++) {
+                       if (((i % 8) == 0) && (i != 0)) {
+                               copy_info(&info, "\n");
+                       }
+                       copy_info(&info, "0x%.4x ",
+                                 ((uint16_t*)ahc->seep_config)[i]);
+               }
+               copy_info(&info, "\n");
+       }
+       copy_info(&info, "\n");
+
+       max_targ = 15;
+       if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
+               max_targ = 7;
+
+       for (i = 0; i <= max_targ; i++) {
+               u_int our_id;
+               u_int target_id;
+               char channel;
+
+               channel = 'A';
+               our_id = ahc->our_id;
+               target_id = i;
+               if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+                       channel = 'B';
+                       our_id = ahc->our_id_b;
+                       target_id = i % 8;
+               }
+
+               ahc_dump_target_state(ahc, &info, our_id,
+                                     channel, target_id, i);
+       }
+       retval = info.pos > info.offset ? info.pos - info.offset : 0;
+done:
+       ahc_list_unlock(&s);
+       return (retval);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_reg.h b/xen/drivers/scsi/aic7xxx/aic7xxx_reg.h
new file mode 100644 (file)
index 0000000..a82c3ae
--- /dev/null
@@ -0,0 +1,722 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ *              from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#43 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#30 $
+ */
+
+#define        SCSISEQ                         0x00
+#define                TEMODE                  0x80
+#define                SCSIRSTO                0x01
+
+#define        SXFRCTL0                        0x01
+#define                DFON                    0x80
+#define                DFPEXP                  0x40
+#define                FAST20                  0x20
+#define                CLRSTCNT                0x10
+#define                SPIOEN                  0x08
+#define                SCAMEN                  0x04
+#define                CLRCHN                  0x02
+
+#define        SXFRCTL1                        0x02
+#define                BITBUCKET               0x80
+#define                SWRAPEN                 0x40
+#define                STIMESEL                0x18
+#define                ENSTIMER                0x04
+#define                ACTNEGEN                0x02
+#define                STPWEN                  0x01
+
+#define        SCSISIGO                        0x03
+#define                CDO                     0x80
+#define                IOO                     0x40
+#define                MSGO                    0x20
+#define                ATNO                    0x10
+#define                SELO                    0x08
+#define                BSYO                    0x04
+#define                REQO                    0x02
+#define                ACKO                    0x01
+
+#define        SCSISIGI                        0x03
+#define                P_DATAIN_DT             0x60
+#define                P_DATAOUT_DT            0x20
+#define                ATNI                    0x10
+#define                SELI                    0x08
+#define                BSYI                    0x04
+#define                REQI                    0x02
+#define                ACKI                    0x01
+
+#define        SCSIRATE                        0x04
+#define                WIDEXFER                0x80
+#define                SXFR                    0x70
+#define                ENABLE_CRC              0x40
+#define                SINGLE_EDGE             0x10
+#define                SOFS                    0x0f
+#define                SXFR_ULTRA2             0x0f
+
+#define        SCSIID                          0x05
+#define        SCSIOFFSET                      0x05
+#define                SOFS_ULTRA2             0x7f
+
+#define        SCSIDATL                        0x06
+
+#define        SCSIDATH                        0x07
+
+#define        STCNT                           0x08
+
+#define        OPTIONMODE                      0x08
+#define                AUTORATEEN              0x80
+#define                AUTOACKEN               0x40
+#define                ATNMGMNTEN              0x20
+#define                BUSFREEREV              0x10
+#define                EXPPHASEDIS             0x08
+#define                SCSIDATL_IMGEN          0x04
+#define                OPTIONMODE_DEFAULTS     0x03
+#define                AUTO_MSGOUT_DE          0x02
+#define                DIS_MSGIN_DUALEDGE      0x01
+
+#define        TARGCRCCNT                      0x0a
+
+#define        CLRSINT0                        0x0b
+#define                CLRSELDO                0x40
+#define                CLRSELDI                0x20
+#define                CLRSELINGO              0x10
+#define                CLRIOERR                0x08
+#define                CLRSWRAP                0x08
+#define                CLRSPIORDY              0x02
+
+#define        SSTAT0                          0x0b
+#define                TARGET                  0x80
+#define                SELDO                   0x40
+#define                SELDI                   0x20
+#define                SELINGO                 0x10
+#define                SWRAP                   0x08
+#define                IOERR                   0x08
+#define                SDONE                   0x04
+#define                SPIORDY                 0x02
+#define                DMADONE                 0x01
+
+#define        CLRSINT1                        0x0c
+#define                CLRSELTIMEO             0x80
+#define                CLRATNO                 0x40
+#define                CLRSCSIRSTI             0x20
+#define                CLRBUSFREE              0x08
+#define                CLRSCSIPERR             0x04
+#define                CLRPHASECHG             0x02
+#define                CLRREQINIT              0x01
+
+#define        SSTAT1                          0x0c
+#define                SELTO                   0x80
+#define                ATNTARG                 0x40
+#define                SCSIRSTI                0x20
+#define                PHASEMIS                0x10
+#define                BUSFREE                 0x08
+#define                SCSIPERR                0x04
+#define                PHASECHG                0x02
+#define                REQINIT                 0x01
+
+#define        SSTAT2                          0x0d
+#define                OVERRUN                 0x80
+#define                SHVALID                 0x40
+#define                SFCNT                   0x1f
+#define                EXP_ACTIVE              0x10
+#define                CRCVALERR               0x08
+#define                CRCENDERR               0x04
+#define                CRCREQERR               0x02
+#define                DUAL_EDGE_ERR           0x01
+
+#define        SSTAT3                          0x0e
+#define                SCSICNT                 0xf0
+#define                U2OFFCNT                0x7f
+#define                OFFCNT                  0x0f
+
+#define        SCSIID_ULTRA2                   0x0f
+
+#define        SIMODE0                         0x10
+#define                ENSELDO                 0x40
+#define                ENSELDI                 0x20
+#define                ENSELINGO               0x10
+#define                ENIOERR                 0x08
+#define                ENSWRAP                 0x08
+#define                ENSDONE                 0x04
+#define                ENSPIORDY               0x02
+#define                ENDMADONE               0x01
+
+#define        SIMODE1                         0x11
+#define                ENSELTIMO               0x80
+#define                ENATNTARG               0x40
+#define                ENSCSIRST               0x20
+#define                ENPHASEMIS              0x10
+#define                ENBUSFREE               0x08
+#define                ENSCSIPERR              0x04
+#define                ENPHASECHG              0x02
+#define                ENREQINIT               0x01
+
+#define        SCSIBUSL                        0x12
+
+#define        SCSIBUSH                        0x13
+
+#define        SHADDR                          0x14
+
+#define        SELTIMER                        0x18
+#define        TARGIDIN                        0x18
+#define                STAGE6                  0x20
+#define                STAGE5                  0x10
+#define                STAGE4                  0x08
+#define                STAGE3                  0x04
+#define                STAGE2                  0x02
+#define                STAGE1                  0x01
+
+#define        SELID                           0x19
+#define                SELID_MASK              0xf0
+#define                ONEBIT                  0x08
+
+#define        SCAMCTL                         0x1a
+#define                ENSCAMSELO              0x80
+#define                CLRSCAMSELID            0x40
+#define                ALTSTIM                 0x20
+#define                DFLTTID                 0x10
+#define                SCAMLVL                 0x03
+
+#define        TARGID                          0x1b
+
+#define        SPIOCAP                         0x1b
+#define                SOFT1                   0x80
+#define                SOFT0                   0x40
+#define                SOFTCMDEN               0x20
+#define                EXT_BRDCTL              0x10
+#define                SEEPROM                 0x08
+#define                EEPROM                  0x04
+#define                ROM                     0x02
+#define                SSPIOCPS                0x01
+
+#define        BRDCTL                          0x1d
+#define                BRDDAT7                 0x80
+#define                BRDDAT6                 0x40
+#define                BRDDAT5                 0x20
+#define                BRDDAT4                 0x10
+#define                BRDSTB                  0x10
+#define                BRDDAT3                 0x08
+#define                BRDCS                   0x08
+#define                BRDDAT2                 0x04
+#define                BRDRW                   0x04
+#define                BRDRW_ULTRA2            0x02
+#define                BRDCTL1                 0x02
+#define                BRDCTL0                 0x01
+#define                BRDSTB_ULTRA2           0x01
+
+#define        SEECTL                          0x1e
+#define                EXTARBACK               0x80
+#define                EXTARBREQ               0x40
+#define                SEEMS                   0x20
+#define                SEERDY                  0x10
+#define                SEECS                   0x08
+#define                SEECK                   0x04
+#define                SEEDO                   0x02
+#define                SEEDI                   0x01
+
+#define        SBLKCTL                         0x1f
+#define                DIAGLEDEN               0x80
+#define                DIAGLEDON               0x40
+#define                AUTOFLUSHDIS            0x20
+#define                ENAB40                  0x08
+#define                SELBUSB                 0x08
+#define                ENAB20                  0x04
+#define                SELWIDE                 0x02
+#define                XCVR                    0x01
+
+#define        BUSY_TARGETS                    0x20
+#define        TARG_SCSIRATE                   0x20
+
+#define        ULTRA_ENB                       0x30
+#define        CMDSIZE_TABLE                   0x30
+
+#define        DISC_DSB                        0x32
+
+#define        CMDSIZE_TABLE_TAIL              0x34
+
+#define        MWI_RESIDUAL                    0x38
+
+#define        NEXT_QUEUED_SCB                 0x39
+
+#define        MSG_OUT                         0x3a
+
+#define        DMAPARAMS                       0x3b
+#define                PRELOADEN               0x80
+#define                WIDEODD                 0x40
+#define                SCSIEN                  0x20
+#define                SDMAEN                  0x10
+#define                SDMAENACK               0x10
+#define                HDMAEN                  0x08
+#define                HDMAENACK               0x08
+#define                DIRECTION               0x04
+#define                FIFOFLUSH               0x02
+#define                FIFORESET               0x01
+
+#define        SEQ_FLAGS                       0x3c
+#define                IDENTIFY_SEEN           0x80
+#define                TARGET_CMD_IS_TAGGED    0x40
+#define                DPHASE                  0x20
+#define                TARG_CMD_PENDING        0x10
+#define                CMDPHASE_PENDING        0x08
+#define                DPHASE_PENDING          0x04
+#define                SPHASE_PENDING          0x02
+#define                NO_DISCONNECT           0x01
+
+#define        SAVED_SCSIID                    0x3d
+
+#define        SAVED_LUN                       0x3e
+
+#define        LASTPHASE                       0x3f
+#define                P_MESGIN                0xe0
+#define                PHASE_MASK              0xe0
+#define                P_STATUS                0xc0
+#define                P_MESGOUT               0xa0
+#define                P_COMMAND               0x80
+#define                CDI                     0x80
+#define                P_DATAIN                0x40
+#define                IOI                     0x40
+#define                MSGI                    0x20
+#define                P_BUSFREE               0x01
+#define                P_DATAOUT               0x00
+
+#define        WAITING_SCBH                    0x40
+
+#define        DISCONNECTED_SCBH               0x41
+
+#define        FREE_SCBH                       0x42
+
+#define        COMPLETE_SCBH                   0x43
+
+#define        HSCB_ADDR                       0x44
+
+#define        SHARED_DATA_ADDR                0x48
+
+#define        KERNEL_QINPOS                   0x4c
+
+#define        QINPOS                          0x4d
+
+#define        QOUTPOS                         0x4e
+
+#define        KERNEL_TQINPOS                  0x4f
+
+#define        TQINPOS                         0x50
+
+#define        ARG_1                           0x51
+#define        RETURN_1                        0x51
+#define                SEND_MSG                0x80
+#define                SEND_SENSE              0x40
+#define                SEND_REJ                0x20
+#define                MSGOUT_PHASEMIS         0x10
+#define                EXIT_MSG_LOOP           0x08
+#define                CONT_MSG_LOOP           0x04
+#define                CONT_TARG_SESSION       0x02
+
+#define        ARG_2                           0x52
+#define        RETURN_2                        0x52
+
+#define        LAST_MSG                        0x53
+
+#define        SCSISEQ_TEMPLATE                0x54
+#define                ENSELO                  0x40
+#define                ENSELI                  0x20
+#define                ENRSELI                 0x10
+#define                ENAUTOATNO              0x08
+#define                ENAUTOATNI              0x04
+#define                ENAUTOATNP              0x02
+
+#define        DATA_COUNT_ODD                  0x55
+
+#define        HA_274_BIOSGLOBAL               0x56
+#define        INITIATOR_TAG                   0x56
+#define                HA_274_EXTENDED_TRANS   0x01
+
+#define        SEQ_FLAGS2                      0x57
+#define                TARGET_MSG_PENDING      0x02
+#define                SCB_DMA                 0x01
+
+#define        SCSICONF                        0x5a
+#define                TERM_ENB                0x80
+#define                RESET_SCSI              0x40
+#define                ENSPCHK                 0x20
+#define                HWSCSIID                0x0f
+#define                HSCSIID                 0x07
+
+#define        INTDEF                          0x5c
+#define                EDGE_TRIG               0x80
+#define                VECTOR                  0x0f
+
+#define        HOSTCONF                        0x5d
+
+#define        HA_274_BIOSCTRL                 0x5f
+#define                BIOSDISABLED            0x30
+#define                BIOSMODE                0x30
+#define                CHANNEL_B_PRIMARY       0x08
+
+#define        SEQCTL                          0x60
+#define                PERRORDIS               0x80
+#define                PAUSEDIS                0x40
+#define                FAILDIS                 0x20
+#define                FASTMODE                0x10
+#define                BRKADRINTEN             0x08
+#define                STEP                    0x04
+#define                SEQRESET                0x02
+#define                LOADRAM                 0x01
+
+#define        SEQRAM                          0x61
+
+#define        SEQADDR0                        0x62
+
+#define        SEQADDR1                        0x63
+#define                SEQADDR1_MASK           0x01
+
+#define        ACCUM                           0x64
+
+#define        SINDEX                          0x65
+
+#define        DINDEX                          0x66
+
+#define        ALLONES                         0x69
+
+#define        ALLZEROS                        0x6a
+
+#define        NONE                            0x6a
+
+#define        FLAGS                           0x6b
+#define                ZERO                    0x02
+#define                CARRY                   0x01
+
+#define        SINDIR                          0x6c
+
+#define        DINDIR                          0x6d
+
+#define        FUNCTION1                       0x6e
+
+#define        STACK                           0x6f
+
+#define        TARG_OFFSET                     0x70
+
+#define        SRAM_BASE                       0x70
+
+#define        BCTL                            0x84
+#define                ACE                     0x08
+#define                ENABLE                  0x01
+
+#define        DSCOMMAND0                      0x84
+#define                CACHETHEN               0x80
+#define                DPARCKEN                0x40
+#define                MPARCKEN                0x20
+#define                EXTREQLCK               0x10
+#define                INTSCBRAMSEL            0x08
+#define                RAMPS                   0x04
+#define                USCBSIZE32              0x02
+#define                CIOPARCKEN              0x01
+
+#define        BUSTIME                         0x85
+#define                BOFF                    0xf0
+#define                BON                     0x0f
+
+#define        DSCOMMAND1                      0x85
+#define                DSLATT                  0xfc
+#define                HADDLDSEL1              0x02
+#define                HADDLDSEL0              0x01
+
+#define        BUSSPD                          0x86
+#define                DFTHRSH                 0xc0
+#define                DFTHRSH_75              0x80
+#define                STBOFF                  0x38
+#define                STBON                   0x07
+
+#define        HS_MAILBOX                      0x86
+#define                HOST_MAILBOX            0xf0
+#define                HOST_TQINPOS            0x80
+#define                SEQ_MAILBOX             0x0f
+
+#define        DSPCISTATUS                     0x86
+#define                DFTHRSH_100             0xc0
+
+#define        HCNTRL                          0x87
+#define                POWRDN                  0x40
+#define                SWINT                   0x10
+#define                IRQMS                   0x08
+#define                PAUSE                   0x04
+#define                INTEN                   0x02
+#define                CHIPRST                 0x01
+#define                CHIPRSTACK              0x01
+
+#define        HADDR                           0x88
+
+#define        HCNT                            0x8c
+
+#define        SCBPTR                          0x90
+
+#define        INTSTAT                         0x91
+#define                SEQINT_MASK             0xf1
+#define                OUT_OF_RANGE            0xe1
+#define                NO_FREE_SCB             0xd1
+#define                SCB_MISMATCH            0xc1
+#define                MISSED_BUSFREE          0xb1
+#define                MKMSG_FAILED            0xa1
+#define                DATA_OVERRUN            0x91
+#define                PERR_DETECTED           0x81
+#define                BAD_STATUS              0x71
+#define                HOST_MSG_LOOP           0x61
+#define                PDATA_REINIT            0x51
+#define                IGN_WIDE_RES            0x41
+#define                NO_MATCH                0x31
+#define                NO_IDENT                0x21
+#define                SEND_REJECT             0x11
+#define                INT_PEND                0x0f
+#define                BRKADRINT               0x08
+#define                SCSIINT                 0x04
+#define                CMDCMPLT                0x02
+#define                BAD_PHASE               0x01
+#define                SEQINT                  0x01
+
+#define        CLRINT                          0x92
+#define                CLRPARERR               0x10
+#define                CLRBRKADRINT            0x08
+#define                CLRSCSIINT              0x04
+#define                CLRCMDINT               0x02
+#define                CLRSEQINT               0x01
+
+#define        ERROR                           0x92
+#define                CIOPARERR               0x80
+#define                PCIERRSTAT              0x40
+#define                MPARERR                 0x20
+#define                DPARERR                 0x10
+#define                SQPARERR                0x08
+#define                ILLOPCODE               0x04
+#define                ILLSADDR                0x02
+#define                ILLHADDR                0x01
+
+#define        DFCNTRL                         0x93
+
+#define        DFSTATUS                        0x94
+#define                PRELOAD_AVAIL           0x80
+#define                DFCACHETH               0x40
+#define                FIFOQWDEMP              0x20
+#define                MREQPEND                0x10
+#define                HDONE                   0x08
+#define                DFTHRESH                0x04
+#define                FIFOFULL                0x02
+#define                FIFOEMP                 0x01
+
+#define        DFWADDR                         0x95
+
+#define        DFRADDR                         0x97
+
+#define        DFDAT                           0x99
+
+#define        SCBCNT                          0x9a
+#define                SCBAUTO                 0x80
+#define                SCBCNT_MASK             0x1f
+
+#define        QINFIFO                         0x9b
+
+#define        QINCNT                          0x9c
+
+#define        QOUTFIFO                        0x9d
+
+#define        CRCCONTROL1                     0x9d
+#define                CRCONSEEN               0x80
+#define                CRCVALCHKEN             0x40
+#define                CRCENDCHKEN             0x20
+#define                CRCREQCHKEN             0x10
+#define                TARGCRCENDEN            0x08
+#define                TARGCRCCNTEN            0x04
+
+#define        QOUTCNT                         0x9e
+
+#define        SCSIPHASE                       0x9e
+#define                STATUS_PHASE            0x20
+#define                COMMAND_PHASE           0x10
+#define                MSG_IN_PHASE            0x08
+#define                MSG_OUT_PHASE           0x04
+#define                DATA_PHASE_MASK         0x03
+#define                DATA_IN_PHASE           0x02
+#define                DATA_OUT_PHASE          0x01
+
+#define        SFUNCT                          0x9f
+#define                ALT_MODE                0x80
+
+#define        SCB_BASE                        0xa0
+
+#define        SCB_CDB_PTR                     0xa0
+#define        SCB_RESIDUAL_DATACNT            0xa0
+#define        SCB_CDB_STORE                   0xa0
+
+#define        SCB_RESIDUAL_SGPTR              0xa4
+
+#define        SCB_SCSI_STATUS                 0xa8
+
+#define        SCB_TARGET_PHASES               0xa9
+
+#define        SCB_TARGET_DATA_DIR             0xaa
+
+#define        SCB_TARGET_ITAG                 0xab
+
+#define        SCB_DATAPTR                     0xac
+
+#define        SCB_DATACNT                     0xb0
+#define                SG_LAST_SEG             0x80
+#define                SG_HIGH_ADDR_BITS       0x7f
+
+#define        SCB_SGPTR                       0xb4
+#define                SG_RESID_VALID          0x04
+#define                SG_FULL_RESID           0x02
+#define                SG_LIST_NULL            0x01
+
+#define        SCB_CONTROL                     0xb8
+#define                TARGET_SCB              0x80
+#define                DISCENB                 0x40
+#define                TAG_ENB                 0x20
+#define                MK_MESSAGE              0x10
+#define                ULTRAENB                0x08
+#define                DISCONNECTED            0x04
+#define                SCB_TAG_TYPE            0x03
+
+#define        SCB_SCSIID                      0xb9
+#define                TID                     0xf0
+#define                TWIN_CHNLB              0x80
+#define                TWIN_TID                0x70
+#define                OID                     0x0f
+
+#define        SCB_LUN                         0xba
+#define                LID                     0xff
+
+#define        SCB_TAG                         0xbb
+
+#define        SCB_CDB_LEN                     0xbc
+
+#define        SCB_SCSIRATE                    0xbd
+
+#define        SCB_SCSIOFFSET                  0xbe
+
+#define        SCB_NEXT                        0xbf
+
+#define        SCB_64_SPARE                    0xc0
+
+#define        SEECTL_2840                     0xc0
+#define                CS_2840                 0x04
+#define                CK_2840                 0x02
+#define                DO_2840                 0x01
+
+#define        STATUS_2840                     0xc1
+#define                EEPROM_TF               0x80
+#define                BIOS_SEL                0x60
+#define                ADSEL                   0x1e
+#define                DI_2840                 0x01
+
+#define        SCB_64_BTT                      0xd0
+
+#define        CCHADDR                         0xe0
+
+#define        CCHCNT                          0xe8
+
+#define        CCSGRAM                         0xe9
+
+#define        CCSGADDR                        0xea
+
+#define        CCSGCTL                         0xeb
+#define                CCSGDONE                0x80
+#define                CCSGEN                  0x08
+#define                SG_FETCH_NEEDED         0x02
+#define                CCSGRESET               0x01
+
+#define        CCSCBRAM                        0xec
+
+#define        CCSCBADDR                       0xed
+
+#define        CCSCBCTL                        0xee
+#define                CCSCBDONE               0x80
+#define                ARRDONE                 0x40
+#define                CCARREN                 0x10
+#define                CCSCBEN                 0x08
+#define                CCSCBDIR                0x04
+#define                CCSCBRESET              0x01
+
+#define        CCSCBCNT                        0xef
+
+#define        SCBBADDR                        0xf0
+
+#define        CCSCBPTR                        0xf1
+
+#define        HNSCB_QOFF                      0xf4
+
+#define        SNSCB_QOFF                      0xf6
+
+#define        SDSCB_QOFF                      0xf8
+
+#define        QOFF_CTLSTA                     0xfa
+#define                SCB_AVAIL               0x40
+#define                SNSCB_ROLLOVER          0x20
+#define                SDSCB_ROLLOVER          0x10
+#define                SCB_QSIZE               0x07
+#define                SCB_QSIZE_256           0x06
+
+#define        DFF_THRSH                       0xfb
+#define                WR_DFTHRSH              0x70
+#define                WR_DFTHRSH_MAX          0x70
+#define                WR_DFTHRSH_90           0x60
+#define                WR_DFTHRSH_85           0x50
+#define                WR_DFTHRSH_75           0x40
+#define                WR_DFTHRSH_63           0x30
+#define                WR_DFTHRSH_50           0x20
+#define                WR_DFTHRSH_25           0x10
+#define                RD_DFTHRSH              0x07
+#define                RD_DFTHRSH_MAX          0x07
+#define                RD_DFTHRSH_90           0x06
+#define                RD_DFTHRSH_85           0x05
+#define                RD_DFTHRSH_75           0x04
+#define                RD_DFTHRSH_63           0x03
+#define                RD_DFTHRSH_50           0x02
+#define                RD_DFTHRSH_25           0x01
+#define                RD_DFTHRSH_MIN          0x00
+#define                WR_DFTHRSH_MIN          0x00
+
+#define        SG_CACHE_SHADOW                 0xfc
+#define                SG_ADDR_MASK            0xf8
+#define                ODD_SEG                 0x04
+#define                LAST_SEG                0x02
+#define                LAST_SEG_DONE           0x01
+
+#define        SG_CACHE_PRE                    0xfc
+
+
+#define        MAX_OFFSET_ULTRA2       0x7f
+#define        MAX_OFFSET_16BIT        0x08
+#define        BUS_8_BIT       0x00
+#define        TARGET_CMD_CMPLT        0xfe
+#define        STATUS_QUEUE_FULL       0x28
+#define        STATUS_BUSY     0x08
+#define        MAX_OFFSET_8BIT 0x0f
+#define        BUS_32_BIT      0x02
+#define        CCSGADDR_MAX    0x80
+#define        TID_SHIFT       0x04
+#define        SCB_DOWNLOAD_SIZE_64    0x30
+#define        HOST_MAILBOX_SHIFT      0x04
+#define        CMD_GROUP_CODE_SHIFT    0x05
+#define        CCSGRAM_MAXSEGS 0x10
+#define        SCB_LIST_NULL   0xff
+#define        SG_SIZEOF       0x08
+#define        SCB_DOWNLOAD_SIZE       0x20
+#define        SEQ_MAILBOX_SHIFT       0x00
+#define        TARGET_DATA_IN  0x01
+#define        HOST_MSG        0xff
+#define        BUS_16_BIT      0x01
+#define        SCB_UPLOAD_SIZE 0x20
+
+
+/* Downloaded Constant Definitions */
+#define        INVERTED_CACHESIZE_MASK 0x03
+#define        SG_PREFETCH_ADDR_MASK   0x06
+#define        SG_PREFETCH_ALIGN_MASK  0x05
+#define        QOUTFIFO_OFFSET 0x00
+#define        SG_PREFETCH_CNT 0x04
+#define        CACHESIZE_MASK  0x02
+#define        QINFIFO_OFFSET  0x01
+#define        DOWNLOAD_CONST_COUNT    0x07
+
+
+/* Exported Labels */
diff --git a/xen/drivers/scsi/aic7xxx/aic7xxx_seq.h b/xen/drivers/scsi/aic7xxx/aic7xxx_seq.h
new file mode 100644 (file)
index 0000000..a31de71
--- /dev/null
@@ -0,0 +1,1301 @@
+/*
+ * DO NOT EDIT - This file is automatically generated
+ *              from the following source files:
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#43 $
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#30 $
+ */
+static uint8_t seqprog[] = {
+       0xb2, 0x00, 0x00, 0x08,
+       0xf7, 0x11, 0x22, 0x08,
+       0x00, 0x65, 0xe4, 0x59,
+       0xf7, 0x01, 0x02, 0x08,
+       0xff, 0x6a, 0x24, 0x08,
+       0x40, 0x00, 0x40, 0x68,
+       0x08, 0x1f, 0x3e, 0x10,
+       0x40, 0x00, 0x40, 0x68,
+       0xff, 0x40, 0x3c, 0x60,
+       0x08, 0x1f, 0x3e, 0x10,
+       0x60, 0x0b, 0x42, 0x68,
+       0x40, 0xfa, 0x12, 0x78,
+       0x01, 0x4d, 0xc8, 0x30,
+       0x00, 0x4c, 0x12, 0x70,
+       0x01, 0x39, 0xa2, 0x30,
+       0x00, 0x6a, 0xb6, 0x5e,
+       0x01, 0x51, 0x20, 0x31,
+       0x01, 0x57, 0xae, 0x00,
+       0x0d, 0x6a, 0x76, 0x00,
+       0x00, 0x51, 0x08, 0x5e,
+       0x01, 0x51, 0xc8, 0x30,
+       0x00, 0x39, 0xc8, 0x60,
+       0x00, 0xbb, 0x30, 0x70,
+       0xc1, 0x6a, 0xce, 0x5e,
+       0x01, 0xbf, 0x72, 0x30,
+       0x01, 0x40, 0x7e, 0x31,
+       0x01, 0x90, 0x80, 0x30,
+       0x01, 0xf6, 0xd4, 0x30,
+       0x01, 0x4d, 0x9a, 0x18,
+       0xfe, 0x57, 0xae, 0x08,
+       0x01, 0x40, 0x20, 0x31,
+       0x00, 0x65, 0xcc, 0x58,
+       0x60, 0x0b, 0x40, 0x78,
+       0x08, 0x6a, 0x18, 0x00,
+       0x08, 0x11, 0x22, 0x00,
+       0x60, 0x0b, 0x00, 0x78,
+       0x40, 0x0b, 0xfc, 0x68,
+       0x80, 0x0b, 0xb6, 0x78,
+       0x20, 0x6a, 0x16, 0x00,
+       0xa4, 0x6a, 0x06, 0x00,
+       0x08, 0x3c, 0x78, 0x00,
+       0x01, 0x50, 0xc8, 0x30,
+       0xe0, 0x6a, 0xcc, 0x00,
+       0x48, 0x6a, 0xf2, 0x5d,
+       0x01, 0x6a, 0xdc, 0x01,
+       0x88, 0x6a, 0xcc, 0x00,
+       0x48, 0x6a, 0xf2, 0x5d,
+       0x01, 0x6a, 0x26, 0x01,
+       0xf0, 0x19, 0x7a, 0x08,
+       0x0f, 0x18, 0xc8, 0x08,
+       0x0f, 0x0f, 0xc8, 0x08,
+       0x0f, 0x05, 0xc8, 0x08,
+       0x00, 0x3d, 0x7a, 0x00,
+       0x08, 0x1f, 0x6e, 0x78,
+       0x80, 0x3d, 0x7a, 0x00,
+       0x01, 0x3d, 0xd8, 0x31,
+       0x01, 0x3d, 0x32, 0x31,
+       0x10, 0x03, 0x48, 0x79,
+       0x00, 0x65, 0xf4, 0x58,
+       0x80, 0x66, 0xae, 0x78,
+       0x01, 0x66, 0xd8, 0x31,
+       0x01, 0x66, 0x32, 0x31,
+       0x3f, 0x66, 0x7c, 0x08,
+       0x40, 0x66, 0x82, 0x68,
+       0x01, 0x3c, 0x78, 0x00,
+       0x10, 0x03, 0x9e, 0x78,
+       0x00, 0x65, 0xf4, 0x58,
+       0xe0, 0x66, 0xc8, 0x18,
+       0x00, 0x65, 0xaa, 0x50,
+       0xdd, 0x66, 0xc8, 0x18,
+       0x00, 0x65, 0xaa, 0x48,
+       0x01, 0x66, 0xd8, 0x31,
+       0x01, 0x66, 0x32, 0x31,
+       0x10, 0x03, 0x48, 0x79,
+       0x00, 0x65, 0xf4, 0x58,
+       0x01, 0x66, 0xd8, 0x31,
+       0x01, 0x66, 0x32, 0x31,
+       0x01, 0x66, 0xac, 0x30,
+       0x40, 0x3c, 0x78, 0x00,
+       0xff, 0x6a, 0xd8, 0x01,
+       0xff, 0x6a, 0x32, 0x01,
+       0x90, 0x3c, 0x78, 0x00,
+       0x02, 0x57, 0x3c, 0x69,
+       0x10, 0x03, 0x3a, 0x69,
+       0x00, 0x65, 0x1e, 0x41,
+       0x02, 0x57, 0xae, 0x00,
+       0x00, 0x65, 0x9e, 0x40,
+       0x61, 0x6a, 0xce, 0x5e,
+       0x08, 0x51, 0x1e, 0x71,
+       0x02, 0x0b, 0xb2, 0x78,
+       0x00, 0x65, 0xae, 0x40,
+       0x1a, 0x01, 0x02, 0x00,
+       0xf0, 0x19, 0x7a, 0x08,
+       0x0f, 0x0f, 0xc8, 0x08,
+       0x0f, 0x05, 0xc8, 0x08,
+       0x00, 0x3d, 0x7a, 0x00,
+       0x08, 0x1f, 0xc4, 0x78,
+       0x80, 0x3d, 0x7a, 0x00,
+       0x20, 0x6a, 0x16, 0x00,
+       0x00, 0x65, 0xc4, 0x41,
+       0x00, 0x65, 0xa8, 0x5e,
+       0x00, 0x65, 0x12, 0x40,
+       0x20, 0x11, 0xd2, 0x68,
+       0x20, 0x6a, 0x18, 0x00,
+       0x20, 0x11, 0x22, 0x00,
+       0xf7, 0x1f, 0xca, 0x08,
+       0x80, 0xb9, 0xd8, 0x78,
+       0x08, 0x65, 0xca, 0x00,
+       0x01, 0x65, 0x3e, 0x30,
+       0x01, 0xb9, 0x1e, 0x30,
+       0x7f, 0xb9, 0x0a, 0x08,
+       0x01, 0xb9, 0x0a, 0x30,
+       0x01, 0x54, 0xca, 0x30,
+       0x80, 0xb8, 0xe6, 0x78,
+       0x80, 0x65, 0xca, 0x00,
+       0x01, 0x65, 0x00, 0x34,
+       0x01, 0x54, 0x00, 0x34,
+       0x1a, 0x01, 0x02, 0x00,
+       0x08, 0xb8, 0xf0, 0x78,
+       0x20, 0x01, 0x02, 0x00,
+       0x02, 0xbd, 0x08, 0x34,
+       0x01, 0xbd, 0x08, 0x34,
+       0x08, 0x01, 0x02, 0x00,
+       0x02, 0x0b, 0xf6, 0x78,
+       0xf7, 0x01, 0x02, 0x08,
+       0x01, 0x06, 0xcc, 0x34,
+       0xb2, 0x00, 0x00, 0x08,
+       0x01, 0x40, 0x20, 0x31,
+       0x01, 0xbf, 0x80, 0x30,
+       0x01, 0xb9, 0x7a, 0x30,
+       0x01, 0xba, 0x7c, 0x30,
+       0x00, 0x65, 0xea, 0x58,
+       0x80, 0x0b, 0xbe, 0x79,
+       0xe4, 0x6a, 0x64, 0x5d,
+       0x40, 0x6a, 0x16, 0x00,
+       0x80, 0xba, 0x7a, 0x5d,
+       0x20, 0xb8, 0x16, 0x79,
+       0x20, 0x6a, 0x7a, 0x5d,
+       0x00, 0xab, 0x7a, 0x5d,
+       0x01, 0xa9, 0x78, 0x30,
+       0x10, 0xb8, 0x1e, 0x79,
+       0xe4, 0x6a, 0x64, 0x5d,
+       0x00, 0x65, 0xae, 0x40,
+       0x10, 0x03, 0x38, 0x69,
+       0x08, 0x3c, 0x54, 0x69,
+       0x04, 0x3c, 0x8c, 0x69,
+       0x02, 0x3c, 0x92, 0x69,
+       0x01, 0x3c, 0x3e, 0x79,
+       0x01, 0x6a, 0xa2, 0x30,
+       0x00, 0x65, 0x9e, 0x59,
+       0x04, 0x51, 0x2c, 0x61,
+       0x00, 0x6a, 0xb6, 0x5e,
+       0x0d, 0x6a, 0x76, 0x00,
+       0x00, 0xbb, 0x08, 0x5e,
+       0x00, 0x65, 0xec, 0x58,
+       0x00, 0x65, 0x16, 0x41,
+       0xa4, 0x6a, 0x06, 0x00,
+       0x00, 0x65, 0xf4, 0x58,
+       0x00, 0x65, 0xae, 0x40,
+       0xe4, 0x6a, 0x64, 0x5d,
+       0x20, 0x3c, 0x44, 0x79,
+       0x02, 0x6a, 0x7a, 0x5d,
+       0x04, 0x6a, 0x7a, 0x5d,
+       0x01, 0x03, 0x46, 0x69,
+       0xf7, 0x11, 0x22, 0x08,
+       0xff, 0x6a, 0x24, 0x08,
+       0xff, 0x6a, 0x06, 0x08,
+       0x01, 0x6a, 0x7e, 0x00,
+       0x00, 0x65, 0x9e, 0x59,
+       0x00, 0x65, 0x04, 0x40,
+       0x80, 0x86, 0xc8, 0x08,
+       0x01, 0x4f, 0xc8, 0x30,
+       0x00, 0x50, 0x66, 0x61,
+       0xc4, 0x6a, 0x64, 0x5d,
+       0x40, 0x3c, 0x62, 0x79,
+       0x28, 0x6a, 0x7a, 0x5d,
+       0x00, 0x65, 0x46, 0x41,
+       0x08, 0x6a, 0x7a, 0x5d,
+       0x00, 0x65, 0x46, 0x41,
+       0x84, 0x6a, 0x64, 0x5d,
+       0x00, 0x65, 0xf4, 0x58,
+       0x01, 0x66, 0xc8, 0x30,
+       0x01, 0x64, 0xd8, 0x31,
+       0x01, 0x64, 0x32, 0x31,
+       0x5b, 0x64, 0xc8, 0x28,
+       0x30, 0x64, 0xca, 0x18,
+       0x01, 0x6c, 0xc8, 0x30,
+       0xff, 0x64, 0x88, 0x79,
+       0x08, 0x01, 0x02, 0x00,
+       0x02, 0x0b, 0x7a, 0x79,
+       0x01, 0x64, 0x80, 0x61,
+       0xf7, 0x01, 0x02, 0x08,
+       0x01, 0x06, 0xd8, 0x31,
+       0x01, 0x06, 0x32, 0x31,
+       0xff, 0x64, 0xc8, 0x18,
+       0xff, 0x64, 0x7a, 0x69,
+       0xf7, 0x3c, 0x78, 0x08,
+       0x00, 0x65, 0x1e, 0x41,
+       0x40, 0xaa, 0x7e, 0x10,
+       0x04, 0xaa, 0x64, 0x5d,
+       0x00, 0x65, 0x56, 0x42,
+       0xc4, 0x6a, 0x64, 0x5d,
+       0xc0, 0x6a, 0x7e, 0x00,
+       0x00, 0xa8, 0x7a, 0x5d,
+       0xe4, 0x6a, 0x06, 0x00,
+       0x00, 0x6a, 0x7a, 0x5d,
+       0x00, 0x65, 0x46, 0x41,
+       0x10, 0x3c, 0xa2, 0x69,
+       0x00, 0xbb, 0x84, 0x44,
+       0x18, 0x6a, 0xda, 0x01,
+       0x01, 0x69, 0xd8, 0x31,
+       0x1c, 0x6a, 0xd0, 0x01,
+       0x09, 0xee, 0xdc, 0x01,
+       0x80, 0xee, 0xaa, 0x79,
+       0xff, 0x6a, 0xdc, 0x09,
+       0x01, 0x93, 0x26, 0x01,
+       0x03, 0x6a, 0x2a, 0x01,
+       0x01, 0x69, 0x32, 0x31,
+       0x1c, 0x6a, 0xd6, 0x5d,
+       0x0a, 0x93, 0x26, 0x01,
+       0x00, 0x65, 0x9e, 0x5e,
+       0x01, 0x50, 0xa0, 0x18,
+       0x02, 0x6a, 0x22, 0x05,
+       0x80, 0x6a, 0x74, 0x00,
+       0x80, 0x3c, 0x78, 0x00,
+       0x40, 0x6a, 0x16, 0x00,
+       0x00, 0x65, 0xce, 0x5d,
+       0x01, 0x3f, 0xc8, 0x30,
+       0xbf, 0x64, 0x56, 0x7a,
+       0x80, 0x64, 0xaa, 0x73,
+       0xa0, 0x64, 0x08, 0x74,
+       0xc0, 0x64, 0xfc, 0x73,
+       0xe0, 0x64, 0x38, 0x74,
+       0x01, 0x6a, 0xce, 0x5e,
+       0x00, 0x65, 0xc4, 0x41,
+       0xf7, 0x11, 0x22, 0x08,
+       0x01, 0x06, 0xd4, 0x30,
+       0xff, 0x6a, 0x24, 0x08,
+       0xf7, 0x01, 0x02, 0x08,
+       0x09, 0x0c, 0xde, 0x79,
+       0x08, 0x0c, 0x04, 0x68,
+       0xb1, 0x6a, 0xce, 0x5e,
+       0xff, 0x6a, 0x26, 0x09,
+       0x12, 0x01, 0x02, 0x00,
+       0x02, 0x6a, 0x08, 0x30,
+       0xff, 0x6a, 0x08, 0x08,
+       0xdf, 0x01, 0x02, 0x08,
+       0x01, 0x6a, 0x7e, 0x00,
+       0xff, 0x6a, 0x78, 0x0c,
+       0xff, 0x6a, 0xc8, 0x08,
+       0x08, 0xa4, 0x48, 0x19,
+       0x00, 0xa5, 0x4a, 0x21,
+       0x00, 0xa6, 0x4c, 0x21,
+       0x00, 0xa7, 0x4e, 0x25,
+       0x08, 0xeb, 0xd2, 0x7e,
+       0x80, 0xeb, 0xfe, 0x79,
+       0xff, 0x6a, 0xd6, 0x09,
+       0x08, 0xeb, 0x02, 0x6a,
+       0xff, 0x6a, 0xd4, 0x0c,
+       0x80, 0xa3, 0xd2, 0x6e,
+       0x88, 0xeb, 0x18, 0x72,
+       0x08, 0xeb, 0xd2, 0x6e,
+       0x04, 0xea, 0x1c, 0xe2,
+       0x08, 0xee, 0xd2, 0x6e,
+       0x04, 0x6a, 0xd0, 0x81,
+       0x05, 0xa4, 0xc0, 0x89,
+       0x03, 0xa5, 0xc2, 0x31,
+       0x09, 0x6a, 0xd6, 0x05,
+       0x00, 0x65, 0x00, 0x5a,
+       0x06, 0xa4, 0xd4, 0x89,
+       0x80, 0x94, 0xd2, 0x7e,
+       0x07, 0xe9, 0x10, 0x31,
+       0x01, 0x8c, 0x24, 0x7a,
+       0x01, 0x55, 0xaa, 0x10,
+       0x01, 0xe9, 0x46, 0x31,
+       0x00, 0xa3, 0xb0, 0x5e,
+       0x00, 0x65, 0xf2, 0x59,
+       0x01, 0xa4, 0xca, 0x30,
+       0x01, 0x55, 0x30, 0x7a,
+       0x04, 0x65, 0xca, 0x00,
+       0x80, 0xa3, 0x34, 0x7a,
+       0x02, 0x65, 0xca, 0x00,
+       0x01, 0x65, 0xf8, 0x31,
+       0x80, 0x93, 0x26, 0x01,
+       0xff, 0x6a, 0xd4, 0x0c,
+       0x01, 0x8c, 0xc8, 0x30,
+       0x00, 0x88, 0xc8, 0x18,
+       0x02, 0x64, 0xc8, 0x88,
+       0xff, 0x64, 0xd2, 0x7e,
+       0xff, 0x8d, 0x4a, 0x6a,
+       0xff, 0x8e, 0x4a, 0x6a,
+       0x03, 0x8c, 0xd4, 0x98,
+       0x00, 0x65, 0xd2, 0x56,
+       0x01, 0x64, 0x70, 0x30,
+       0xff, 0x64, 0xc8, 0x10,
+       0x01, 0x64, 0xc8, 0x18,
+       0x00, 0x8c, 0x18, 0x19,
+       0xff, 0x8d, 0x1a, 0x21,
+       0xff, 0x8e, 0x1c, 0x25,
+       0x80, 0x3c, 0x5a, 0x6a,
+       0x21, 0x6a, 0xce, 0x46,
+       0xa8, 0x6a, 0x76, 0x00,
+       0x79, 0x6a, 0x76, 0x00,
+       0x40, 0x3f, 0x62, 0x6a,
+       0x04, 0x3b, 0x76, 0x00,
+       0x04, 0x6a, 0xd4, 0x81,
+       0x20, 0x3c, 0x6a, 0x7a,
+       0x51, 0x6a, 0xce, 0x5e,
+       0x00, 0x65, 0x84, 0x42,
+       0x20, 0x3c, 0x78, 0x00,
+       0x00, 0xb3, 0xb0, 0x5e,
+       0x07, 0xac, 0x10, 0x31,
+       0x05, 0xb3, 0x46, 0x31,
+       0x88, 0x6a, 0xcc, 0x00,
+       0xac, 0x6a, 0xe4, 0x5d,
+       0xa3, 0x6a, 0xcc, 0x00,
+       0xb3, 0x6a, 0xe8, 0x5d,
+       0x00, 0x65, 0x3a, 0x5a,
+       0xfd, 0xa4, 0x48, 0x09,
+       0x01, 0x8c, 0xaa, 0x08,
+       0x03, 0x8c, 0x10, 0x30,
+       0x00, 0x65, 0xdc, 0x5d,
+       0x01, 0xa4, 0x96, 0x7a,
+       0x04, 0x3b, 0x76, 0x08,
+       0x01, 0x3b, 0x26, 0x31,
+       0x80, 0x02, 0x04, 0x00,
+       0x10, 0x0c, 0x8c, 0x7a,
+       0x03, 0x9e, 0x8e, 0x6a,
+       0x7f, 0x02, 0x04, 0x08,
+       0x91, 0x6a, 0xce, 0x5e,
+       0x00, 0x65, 0xc4, 0x41,
+       0x01, 0xa4, 0xca, 0x30,
+       0x80, 0xa3, 0x9c, 0x7a,
+       0x02, 0x65, 0xca, 0x00,
+       0x01, 0x55, 0xa0, 0x7a,
+       0x04, 0x65, 0xca, 0x00,
+       0x01, 0x65, 0xf8, 0x31,
+       0x01, 0x3b, 0x26, 0x31,
+       0x00, 0x65, 0x06, 0x5a,
+       0x01, 0xfc, 0xae, 0x6a,
+       0x80, 0x0b, 0xa4, 0x6a,
+       0x10, 0x0c, 0xa4, 0x7a,
+       0x20, 0x93, 0xa4, 0x6a,
+       0x02, 0x93, 0x26, 0x01,
+       0x02, 0xfc, 0xb8, 0x7a,
+       0x40, 0x0d, 0xd2, 0x6a,
+       0x01, 0xa4, 0x48, 0x01,
+       0x00, 0x65, 0xd2, 0x42,
+       0x40, 0x0d, 0xbe, 0x6a,
+       0x00, 0x65, 0x06, 0x5a,
+       0x00, 0x65, 0xb0, 0x42,
+       0x80, 0xfc, 0xc8, 0x7a,
+       0x80, 0xa4, 0xc8, 0x6a,
+       0xff, 0xa5, 0x4a, 0x19,
+       0xff, 0xa6, 0x4c, 0x21,
+       0xff, 0xa7, 0x4e, 0x21,
+       0xf8, 0xfc, 0x48, 0x09,
+       0xff, 0x6a, 0xaa, 0x08,
+       0x04, 0xfc, 0xd0, 0x7a,
+       0x01, 0x55, 0xaa, 0x00,
+       0xff, 0x6a, 0x46, 0x09,
+       0x04, 0x3b, 0xea, 0x6a,
+       0x02, 0x93, 0x26, 0x01,
+       0x01, 0x94, 0xd4, 0x7a,
+       0x01, 0x94, 0xd4, 0x7a,
+       0x01, 0x94, 0xd4, 0x7a,
+       0x01, 0x94, 0xd4, 0x7a,
+       0x01, 0x94, 0xd4, 0x7a,
+       0x01, 0xa4, 0xe8, 0x7a,
+       0x01, 0xfc, 0xe2, 0x7a,
+       0x01, 0x94, 0xea, 0x6a,
+       0x00, 0x65, 0x84, 0x42,
+       0x01, 0x94, 0xe8, 0x7a,
+       0x10, 0x94, 0xea, 0x6a,
+       0xd7, 0x93, 0x26, 0x09,
+       0x28, 0x93, 0xee, 0x6a,
+       0x01, 0x85, 0x0a, 0x01,
+       0x02, 0xfc, 0xf6, 0x6a,
+       0x01, 0x14, 0x46, 0x31,
+       0xff, 0x6a, 0x10, 0x09,
+       0xfe, 0x85, 0x0a, 0x09,
+       0xff, 0x38, 0x04, 0x6b,
+       0x80, 0xa3, 0x04, 0x7b,
+       0x80, 0x0b, 0x02, 0x7b,
+       0x04, 0x3b, 0x04, 0x7b,
+       0xbf, 0x3b, 0x76, 0x08,
+       0x01, 0x3b, 0x26, 0x31,
+       0x00, 0x65, 0x06, 0x5a,
+       0x01, 0x0b, 0x12, 0x6b,
+       0x10, 0x0c, 0x06, 0x7b,
+       0x04, 0x93, 0x10, 0x6b,
+       0x01, 0x94, 0x0e, 0x7b,
+       0x10, 0x94, 0x10, 0x6b,
+       0xc7, 0x93, 0x26, 0x09,
+       0x01, 0x99, 0xd4, 0x30,
+       0x38, 0x93, 0x14, 0x6b,
+       0xff, 0x08, 0x66, 0x6b,
+       0xff, 0x09, 0x66, 0x6b,
+       0xff, 0x0a, 0x66, 0x6b,
+       0xff, 0x38, 0x30, 0x7b,
+       0x04, 0x14, 0x10, 0x31,
+       0x01, 0x38, 0x18, 0x31,
+       0x02, 0x6a, 0x1a, 0x31,
+       0x88, 0x6a, 0xcc, 0x00,
+       0x14, 0x6a, 0xea, 0x5d,
+       0x00, 0x38, 0xd6, 0x5d,
+       0xff, 0x6a, 0x70, 0x08,
+       0x00, 0x65, 0x5c, 0x43,
+       0x80, 0xa3, 0x36, 0x7b,
+       0x01, 0xa4, 0x48, 0x01,
+       0x00, 0x65, 0x66, 0x43,
+       0x08, 0xeb, 0x3c, 0x7b,
+       0x00, 0x65, 0x06, 0x5a,
+       0x08, 0xeb, 0x38, 0x6b,
+       0x07, 0xe9, 0x10, 0x31,
+       0x01, 0xe9, 0xca, 0x30,
+       0x01, 0x65, 0x46, 0x31,
+       0x00, 0x6a, 0xb0, 0x5e,
+       0x88, 0x6a, 0xcc, 0x00,
+       0xa4, 0x6a, 0xea, 0x5d,
+       0x08, 0x6a, 0xd6, 0x5d,
+       0x0d, 0x93, 0x26, 0x01,
+       0x00, 0x65, 0x9e, 0x5e,
+       0x88, 0x6a, 0xcc, 0x00,
+       0x00, 0x65, 0x80, 0x5e,
+       0x01, 0x99, 0x46, 0x31,
+       0x00, 0xa3, 0xb0, 0x5e,
+       0x01, 0x88, 0x10, 0x31,
+       0x00, 0x65, 0x3a, 0x5a,
+       0x00, 0x65, 0xf2, 0x59,
+       0x03, 0x8c, 0x10, 0x30,
+       0x00, 0x65, 0xdc, 0x5d,
+       0x01, 0x8c, 0x64, 0x7b,
+       0x01, 0x55, 0xaa, 0x10,
+       0x80, 0x0b, 0x84, 0x6a,
+       0x80, 0x0b, 0x6e, 0x6b,
+       0x01, 0x0c, 0x68, 0x7b,
+       0x10, 0x0c, 0x84, 0x7a,
+       0x03, 0x9e, 0x84, 0x6a,
+       0x00, 0x65, 0xfc, 0x59,
+       0x00, 0x6a, 0xb0, 0x5e,
+       0x01, 0xa4, 0x8e, 0x6b,
+       0xff, 0x38, 0x84, 0x7b,
+       0x01, 0x38, 0xc8, 0x30,
+       0x00, 0x08, 0x40, 0x19,
+       0xff, 0x6a, 0xc8, 0x08,
+       0x00, 0x09, 0x42, 0x21,
+       0x00, 0x0a, 0x44, 0x21,
+       0xff, 0x6a, 0x70, 0x08,
+       0x00, 0x65, 0x86, 0x43,
+       0x03, 0x08, 0x40, 0x31,
+       0x03, 0x08, 0x40, 0x31,
+       0x01, 0x08, 0x40, 0x31,
+       0x01, 0x09, 0x42, 0x31,
+       0x01, 0x0a, 0x44, 0x31,
+       0xfd, 0xb4, 0x68, 0x09,
+       0x12, 0x01, 0x02, 0x00,
+       0x12, 0x01, 0x02, 0x00,
+       0x04, 0x3c, 0xc4, 0x79,
+       0xfb, 0x3c, 0x78, 0x08,
+       0x04, 0x93, 0x1e, 0x79,
+       0x01, 0x0c, 0x9a, 0x6b,
+       0x01, 0x55, 0x1e, 0x79,
+       0x80, 0x04, 0x1e, 0x79,
+       0xe4, 0x6a, 0x64, 0x5d,
+       0x23, 0x6a, 0x7a, 0x5d,
+       0x01, 0x6a, 0x7a, 0x5d,
+       0x00, 0x65, 0x1e, 0x41,
+       0x00, 0x65, 0xc4, 0x41,
+       0x80, 0x3c, 0xae, 0x6b,
+       0x21, 0x6a, 0xce, 0x46,
+       0x01, 0xbc, 0x18, 0x31,
+       0x02, 0x6a, 0x1a, 0x31,
+       0x02, 0x6a, 0xf8, 0x01,
+       0x01, 0xbc, 0x10, 0x30,
+       0x02, 0x6a, 0x12, 0x30,
+       0x01, 0xbc, 0x10, 0x30,
+       0xff, 0x6a, 0x12, 0x08,
+       0xff, 0x6a, 0x14, 0x08,
+       0xf3, 0xbc, 0xd4, 0x18,
+       0xa0, 0x6a, 0xd4, 0x53,
+       0x04, 0xa0, 0x10, 0x31,
+       0xac, 0x6a, 0x26, 0x01,
+       0x04, 0xa0, 0x10, 0x31,
+       0x03, 0x08, 0x18, 0x31,
+       0x88, 0x6a, 0xcc, 0x00,
+       0xa0, 0x6a, 0xea, 0x5d,
+       0x00, 0xbc, 0xd6, 0x5d,
+       0x3d, 0x6a, 0x26, 0x01,
+       0x00, 0x65, 0xec, 0x43,
+       0xff, 0x6a, 0x10, 0x09,
+       0xa4, 0x6a, 0x26, 0x01,
+       0x0c, 0xa0, 0x32, 0x31,
+       0x05, 0x6a, 0x26, 0x01,
+       0x35, 0x6a, 0x26, 0x01,
+       0x0c, 0xa0, 0x32, 0x31,
+       0x36, 0x6a, 0x26, 0x01,
+       0x02, 0x93, 0x26, 0x01,
+       0x35, 0x6a, 0x26, 0x01,
+       0x00, 0x65, 0x92, 0x5e,
+       0x00, 0x65, 0x92, 0x5e,
+       0x02, 0x93, 0x26, 0x01,
+       0x04, 0x0b, 0xf0, 0x6b,
+       0x10, 0x0c, 0xec, 0x7b,
+       0x01, 0x03, 0xf0, 0x6b,
+       0x20, 0x93, 0xec, 0x6b,
+       0xc7, 0x93, 0x26, 0x09,
+       0x38, 0x93, 0xf6, 0x6b,
+       0x10, 0x01, 0x02, 0x00,
+       0x00, 0x65, 0xc4, 0x41,
+       0x80, 0x3c, 0x00, 0x6c,
+       0x21, 0x6a, 0xce, 0x46,
+       0x01, 0x06, 0x50, 0x31,
+       0x00, 0x65, 0xc4, 0x41,
+       0x10, 0x3f, 0x06, 0x00,
+       0x10, 0x6a, 0x06, 0x00,
+       0x01, 0x3a, 0xca, 0x30,
+       0x80, 0x65, 0x24, 0x64,
+       0x10, 0xb8, 0x48, 0x6c,
+       0xc0, 0xba, 0xca, 0x00,
+       0x40, 0xb8, 0x14, 0x6c,
+       0xbf, 0x65, 0xca, 0x08,
+       0x20, 0xb8, 0x28, 0x7c,
+       0x01, 0x65, 0x0c, 0x30,
+       0x00, 0x65, 0xce, 0x5d,
+       0xa0, 0x3f, 0x30, 0x64,
+       0x23, 0xb8, 0x0c, 0x08,
+       0x00, 0x65, 0xce, 0x5d,
+       0xa0, 0x3f, 0x30, 0x64,
+       0x00, 0xbb, 0x28, 0x44,
+       0xff, 0x65, 0x28, 0x64,
+       0x00, 0x65, 0x48, 0x44,
+       0x40, 0x6a, 0x18, 0x00,
+       0x01, 0x65, 0x0c, 0x30,
+       0x00, 0x65, 0xce, 0x5d,
+       0xa0, 0x3f, 0x04, 0x74,
+       0x40, 0x6a, 0x18, 0x00,
+       0x01, 0x3a, 0xa6, 0x30,
+       0x08, 0x6a, 0x74, 0x00,
+       0x00, 0x65, 0xc4, 0x41,
+       0x64, 0x6a, 0x5e, 0x5d,
+       0x80, 0x64, 0xce, 0x6c,
+       0x04, 0x64, 0x94, 0x74,
+       0x02, 0x64, 0xa2, 0x74,
+       0x00, 0x6a, 0x64, 0x74,
+       0x03, 0x64, 0xc0, 0x74,
+       0x23, 0x64, 0x50, 0x74,
+       0x08, 0x64, 0x60, 0x74,
+       0x61, 0x6a, 0xce, 0x5e,
+       0x00, 0x65, 0xce, 0x5d,
+       0x08, 0x51, 0xc6, 0x71,
+       0x00, 0x65, 0x48, 0x44,
+       0x80, 0x04, 0x5e, 0x7c,
+       0x51, 0x6a, 0x54, 0x5d,
+       0x01, 0x51, 0x5e, 0x64,
+       0x01, 0xa4, 0x5a, 0x7c,
+       0x01, 0x55, 0x60, 0x7c,
+       0x41, 0x6a, 0xce, 0x5e,
+       0x00, 0x65, 0x60, 0x44,
+       0x07, 0x6a, 0x4a, 0x5d,
+       0x01, 0x06, 0xd4, 0x30,
+       0x00, 0x65, 0xc4, 0x41,
+       0x10, 0xb8, 0x68, 0x7c,
+       0xa1, 0x6a, 0xce, 0x5e,
+       0x01, 0xb4, 0x6e, 0x6c,
+       0x02, 0xb4, 0x70, 0x6c,
+       0x01, 0xa4, 0x70, 0x7c,
+       0xff, 0xa8, 0x80, 0x7c,
+       0x04, 0xb4, 0x68, 0x01,
+       0x01, 0x6a, 0x76, 0x00,
+       0x00, 0xbb, 0x08, 0x5e,
+       0xff, 0xa8, 0x80, 0x7c,
+       0x71, 0x6a, 0xce, 0x5e,
+       0x40, 0x51, 0x80, 0x64,
+       0x00, 0x65, 0xa8, 0x5e,
+       0x00, 0x65, 0xd6, 0x41,
+       0x00, 0xbb, 0x84, 0x5c,
+       0x00, 0x65, 0xd6, 0x41,
+       0x00, 0x65, 0xa8, 0x5e,
+       0x01, 0x65, 0xa2, 0x30,
+       0x01, 0xf8, 0xc8, 0x30,
+       0x01, 0x4e, 0xc8, 0x30,
+       0x00, 0x6a, 0xac, 0xdd,
+       0x00, 0x51, 0xbe, 0x5d,
+       0x01, 0x4e, 0x9c, 0x18,
+       0x02, 0x6a, 0x22, 0x05,
+       0x04, 0xb8, 0x70, 0x01,
+       0x00, 0x65, 0xca, 0x5e,
+       0x20, 0xb8, 0xd6, 0x69,
+       0x01, 0xbb, 0xa2, 0x30,
+       0x01, 0xba, 0x7c, 0x30,
+       0x00, 0xb9, 0xc4, 0x5c,
+       0x00, 0x65, 0xd6, 0x41,
+       0x01, 0x06, 0xd4, 0x30,
+       0x20, 0x3c, 0xc4, 0x79,
+       0x20, 0x3c, 0x60, 0x7c,
+       0x01, 0xa4, 0xb0, 0x7c,
+       0x01, 0xb4, 0x68, 0x01,
+       0x00, 0x65, 0xc4, 0x41,
+       0x00, 0x65, 0x60, 0x44,
+       0x04, 0x14, 0x58, 0x31,
+       0x01, 0x06, 0xd4, 0x30,
+       0x08, 0xa0, 0x60, 0x31,
+       0xac, 0x6a, 0xcc, 0x00,
+       0x14, 0x6a, 0xea, 0x5d,
+       0x01, 0x06, 0xd4, 0x30,
+       0xa0, 0x6a, 0xe2, 0x5d,
+       0x00, 0x65, 0xc4, 0x41,
+       0xdf, 0x3c, 0x78, 0x08,
+       0x00, 0x65, 0x60, 0x44,
+       0x4c, 0x65, 0xcc, 0x28,
+       0x01, 0x3e, 0x20, 0x31,
+       0xd0, 0x66, 0xcc, 0x18,
+       0x20, 0x66, 0xcc, 0x18,
+       0x01, 0x51, 0xda, 0x34,
+       0x4c, 0x3d, 0xca, 0x28,
+       0x3f, 0x64, 0x7c, 0x08,
+       0xd0, 0x65, 0xca, 0x18,
+       0x01, 0x3e, 0x20, 0x31,
+       0x30, 0x65, 0xd4, 0x18,
+       0x00, 0x65, 0xdc, 0x4c,
+       0xe1, 0x6a, 0x22, 0x01,
+       0xff, 0x6a, 0xd4, 0x08,
+       0x20, 0x65, 0xd4, 0x18,
+       0x00, 0x65, 0xe4, 0x54,
+       0xe1, 0x6a, 0x22, 0x01,
+       0xff, 0x6a, 0xd4, 0x08,
+       0x20, 0x65, 0xca, 0x18,
+       0xe0, 0x65, 0xd4, 0x18,
+       0x00, 0x65, 0xee, 0x4c,
+       0xe1, 0x6a, 0x22, 0x01,
+       0xff, 0x6a, 0xd4, 0x08,
+       0xd0, 0x65, 0xd4, 0x18,
+       0x00, 0x65, 0xf6, 0x54,
+       0xe1, 0x6a, 0x22, 0x01,
+       0xff, 0x6a, 0xd4, 0x08,
+       0x01, 0x6c, 0xa2, 0x30,
+       0xff, 0x51, 0x08, 0x75,
+       0x00, 0x51, 0x84, 0x5d,
+       0x01, 0x51, 0x20, 0x31,
+       0x00, 0x65, 0x2a, 0x45,
+       0x01, 0xba, 0xc8, 0x30,
+       0x00, 0x3e, 0x2a, 0x75,
+       0x00, 0x65, 0xa6, 0x5e,
+       0x80, 0x3c, 0x78, 0x00,
+       0x01, 0x06, 0xd4, 0x30,
+       0x00, 0x65, 0xce, 0x5d,
+       0x01, 0x3c, 0x78, 0x00,
+       0xe0, 0x3f, 0x46, 0x65,
+       0x02, 0x3c, 0x78, 0x00,
+       0x20, 0x12, 0x46, 0x65,
+       0x51, 0x6a, 0x54, 0x5d,
+       0x00, 0x51, 0x84, 0x5d,
+       0x51, 0x6a, 0x54, 0x5d,
+       0x01, 0x51, 0x20, 0x31,
+       0x04, 0x3c, 0x78, 0x00,
+       0x01, 0xb9, 0xc8, 0x30,
+       0x00, 0x3d, 0x44, 0x65,
+       0x08, 0x3c, 0x78, 0x00,
+       0x01, 0xba, 0xc8, 0x30,
+       0x00, 0x3e, 0x44, 0x65,
+       0x10, 0x3c, 0x78, 0x00,
+       0x04, 0xb8, 0x44, 0x7d,
+       0xfb, 0xb8, 0x70, 0x09,
+       0x20, 0xb8, 0x3a, 0x6d,
+       0x01, 0x90, 0xc8, 0x30,
+       0xff, 0x6a, 0xa2, 0x00,
+       0x00, 0x3d, 0xc4, 0x5c,
+       0x01, 0x64, 0x20, 0x31,
+       0x80, 0x6a, 0x78, 0x00,
+       0x00, 0x65, 0xec, 0x58,
+       0x10, 0xb8, 0x60, 0x7c,
+       0xff, 0x6a, 0x4a, 0x5d,
+       0x00, 0x65, 0x60, 0x44,
+       0x00, 0x65, 0xa6, 0x5e,
+       0x31, 0x6a, 0xce, 0x5e,
+       0x00, 0x65, 0x60, 0x44,
+       0x10, 0x3f, 0x06, 0x00,
+       0x10, 0x6a, 0x06, 0x00,
+       0x01, 0x65, 0x74, 0x34,
+       0x81, 0x6a, 0xce, 0x5e,
+       0x00, 0x65, 0x56, 0x45,
+       0x01, 0x06, 0xd4, 0x30,
+       0x01, 0x0c, 0x56, 0x7d,
+       0x04, 0x0c, 0x50, 0x6d,
+       0xe0, 0x03, 0x7e, 0x08,
+       0xe0, 0x3f, 0xc4, 0x61,
+       0x01, 0x65, 0xcc, 0x30,
+       0x01, 0x12, 0xda, 0x34,
+       0x01, 0x06, 0xd4, 0x34,
+       0x01, 0x03, 0x64, 0x6d,
+       0x40, 0x03, 0xcc, 0x08,
+       0x01, 0x65, 0x06, 0x30,
+       0x40, 0x65, 0xc8, 0x08,
+       0x00, 0x66, 0x72, 0x75,
+       0x40, 0x65, 0x72, 0x7d,
+       0x00, 0x65, 0x72, 0x5d,
+       0xff, 0x6a, 0xd4, 0x08,
+       0xff, 0x6a, 0xd4, 0x08,
+       0xff, 0x6a, 0xd4, 0x08,
+       0xff, 0x6a, 0xd4, 0x0c,
+       0x08, 0x01, 0x02, 0x00,
+       0x02, 0x0b, 0x7c, 0x7d,
+       0x01, 0x65, 0x0c, 0x30,
+       0x02, 0x0b, 0x80, 0x7d,
+       0xf7, 0x01, 0x02, 0x0c,
+       0x01, 0x65, 0xc8, 0x30,
+       0xff, 0x41, 0xa4, 0x75,
+       0x01, 0x41, 0x20, 0x31,
+       0xff, 0x6a, 0xa4, 0x00,
+       0x00, 0x65, 0x94, 0x45,
+       0xff, 0xbf, 0xa4, 0x75,
+       0x01, 0x90, 0xa4, 0x30,
+       0x01, 0xbf, 0x20, 0x31,
+       0x00, 0xbb, 0x8e, 0x65,
+       0xff, 0x52, 0xa2, 0x75,
+       0x01, 0xbf, 0xcc, 0x30,
+       0x01, 0x90, 0xca, 0x30,
+       0x01, 0x52, 0x20, 0x31,
+       0x01, 0x66, 0x7e, 0x31,
+       0x01, 0x65, 0x20, 0x35,
+       0x01, 0xbf, 0x82, 0x34,
+       0x01, 0x64, 0xa2, 0x30,
+       0x00, 0x6a, 0xb6, 0x5e,
+       0x0d, 0x6a, 0x76, 0x00,
+       0x00, 0x51, 0x08, 0x46,
+       0x01, 0x65, 0xa4, 0x30,
+       0xe0, 0x6a, 0xcc, 0x00,
+       0x48, 0x6a, 0xfc, 0x5d,
+       0x01, 0x6a, 0xd0, 0x01,
+       0x01, 0x6a, 0xdc, 0x05,
+       0x88, 0x6a, 0xcc, 0x00,
+       0x48, 0x6a, 0xfc, 0x5d,
+       0x01, 0x6a, 0xd6, 0x5d,
+       0x01, 0x6a, 0x26, 0x05,
+       0x01, 0x65, 0xd8, 0x31,
+       0x09, 0xee, 0xdc, 0x01,
+       0x80, 0xee, 0xc2, 0x7d,
+       0xff, 0x6a, 0xdc, 0x0d,
+       0x01, 0x65, 0x32, 0x31,
+       0x0a, 0x93, 0x26, 0x01,
+       0x00, 0x65, 0x9e, 0x46,
+       0x81, 0x6a, 0xce, 0x5e,
+       0x01, 0x0c, 0xce, 0x7d,
+       0x04, 0x0c, 0xcc, 0x6d,
+       0xe0, 0x03, 0x06, 0x08,
+       0xe0, 0x03, 0x7e, 0x0c,
+       0x01, 0x65, 0x18, 0x31,
+       0xff, 0x6a, 0x1a, 0x09,
+       0xff, 0x6a, 0x1c, 0x0d,
+       0x01, 0x8c, 0x10, 0x30,
+       0x01, 0x8d, 0x12, 0x30,
+       0x01, 0x8e, 0x14, 0x34,
+       0x01, 0x6c, 0xda, 0x30,
+       0x01, 0x6c, 0xda, 0x30,
+       0x01, 0x6c, 0xda, 0x30,
+       0x01, 0x6c, 0xda, 0x30,
+       0x01, 0x6c, 0xda, 0x30,
+       0x01, 0x6c, 0xda, 0x30,
+       0x01, 0x6c, 0xda, 0x30,
+       0x01, 0x6c, 0xda, 0x34,
+       0x3d, 0x64, 0xa4, 0x28,
+       0x55, 0x64, 0xc8, 0x28,
+       0x00, 0x65, 0xfc, 0x45,
+       0x2e, 0x64, 0xa4, 0x28,
+       0x66, 0x64, 0xc8, 0x28,
+       0x00, 0x6c, 0xda, 0x18,
+       0x01, 0x52, 0xc8, 0x30,
+       0x00, 0x6c, 0xda, 0x20,
+       0xff, 0x6a, 0xc8, 0x08,
+       0x00, 0x6c, 0xda, 0x20,
+       0x00, 0x6c, 0xda, 0x24,
+       0x01, 0x65, 0xc8, 0x30,
+       0xe0, 0x6a, 0xcc, 0x00,
+       0x44, 0x6a, 0xf8, 0x5d,
+       0x01, 0x90, 0xe2, 0x31,
+       0x04, 0x3b, 0x1c, 0x7e,
+       0x30, 0x6a, 0xd0, 0x01,
+       0x20, 0x6a, 0xd0, 0x01,
+       0x1d, 0x6a, 0xdc, 0x01,
+       0xdc, 0xee, 0x18, 0x66,
+       0x00, 0x65, 0x34, 0x46,
+       0x20, 0x6a, 0xd0, 0x01,
+       0x01, 0x6a, 0xdc, 0x01,
+       0x20, 0xa0, 0xd8, 0x31,
+       0x09, 0xee, 0xdc, 0x01,
+       0x80, 0xee, 0x24, 0x7e,
+       0x11, 0x6a, 0xdc, 0x01,
+       0x50, 0xee, 0x28, 0x66,
+       0x20, 0x6a, 0xd0, 0x01,
+       0x09, 0x6a, 0xdc, 0x01,
+       0x88, 0xee, 0x2e, 0x66,
+       0x19, 0x6a, 0xdc, 0x01,
+       0xd8, 0xee, 0x32, 0x66,
+       0xff, 0x6a, 0xdc, 0x09,
+       0x18, 0xee, 0x36, 0x6e,
+       0xff, 0x6a, 0xd4, 0x0c,
+       0x88, 0x6a, 0xcc, 0x00,
+       0x44, 0x6a, 0xf8, 0x5d,
+       0x20, 0x6a, 0xd6, 0x5d,
+       0x01, 0x3b, 0x26, 0x31,
+       0x04, 0x3b, 0x50, 0x6e,
+       0xa0, 0x6a, 0xca, 0x00,
+       0x20, 0x65, 0xc8, 0x18,
+       0x00, 0x65, 0x8e, 0x5e,
+       0x00, 0x65, 0x48, 0x66,
+       0x0a, 0x93, 0x26, 0x01,
+       0x00, 0x65, 0x9e, 0x46,
+       0xa0, 0x6a, 0xcc, 0x00,
+       0xff, 0x6a, 0xc8, 0x08,
+       0x20, 0x94, 0x54, 0x6e,
+       0x10, 0x94, 0x56, 0x6e,
+       0x08, 0x94, 0x70, 0x6e,
+       0x08, 0x94, 0x70, 0x6e,
+       0x08, 0x94, 0x70, 0x6e,
+       0xff, 0x8c, 0xc8, 0x10,
+       0xc1, 0x64, 0xc8, 0x18,
+       0xf8, 0x64, 0xc8, 0x08,
+       0x01, 0x99, 0xda, 0x30,
+       0x00, 0x66, 0x64, 0x66,
+       0xc0, 0x66, 0xa0, 0x76,
+       0x60, 0x66, 0xc8, 0x18,
+       0x3d, 0x64, 0xc8, 0x28,
+       0x00, 0x65, 0x54, 0x46,
+       0xf7, 0x93, 0x26, 0x09,
+       0x08, 0x93, 0x72, 0x6e,
+       0x00, 0x62, 0xc4, 0x18,
+       0x00, 0x65, 0x9e, 0x5e,
+       0x00, 0x65, 0x7e, 0x5e,
+       0x00, 0x65, 0x7e, 0x5e,
+       0x00, 0x65, 0x7e, 0x5e,
+       0x01, 0x99, 0xda, 0x30,
+       0x01, 0x99, 0xda, 0x30,
+       0x01, 0x99, 0xda, 0x30,
+       0x01, 0x99, 0xda, 0x30,
+       0x01, 0x99, 0xda, 0x30,
+       0x01, 0x99, 0xda, 0x30,
+       0x01, 0x99, 0xda, 0x30,
+       0x01, 0x99, 0xda, 0x34,
+       0x01, 0x6c, 0x32, 0x31,
+       0x01, 0x6c, 0x32, 0x31,
+       0x01, 0x6c, 0x32, 0x31,
+       0x01, 0x6c, 0x32, 0x31,
+       0x01, 0x6c, 0x32, 0x31,
+       0x01, 0x6c, 0x32, 0x31,
+       0x01, 0x6c, 0x32, 0x31,
+       0x01, 0x6c, 0x32, 0x35,
+       0x08, 0x94, 0x9e, 0x7e,
+       0xf7, 0x93, 0x26, 0x09,
+       0x08, 0x93, 0xa2, 0x6e,
+       0xff, 0x6a, 0xd4, 0x0c,
+       0x04, 0xb8, 0xca, 0x6e,
+       0x01, 0x42, 0x7e, 0x31,
+       0xff, 0x6a, 0x76, 0x01,
+       0x01, 0x90, 0x84, 0x34,
+       0xff, 0x6a, 0x76, 0x05,
+       0x01, 0x85, 0x0a, 0x01,
+       0x7f, 0x65, 0x10, 0x09,
+       0xfe, 0x85, 0x0a, 0x0d,
+       0xff, 0x42, 0xc6, 0x66,
+       0xff, 0x41, 0xbe, 0x66,
+       0xd1, 0x6a, 0xce, 0x5e,
+       0xff, 0x6a, 0xca, 0x04,
+       0x01, 0x41, 0x20, 0x31,
+       0x01, 0xbf, 0x82, 0x30,
+       0x01, 0x6a, 0x76, 0x00,
+       0x00, 0xbb, 0x08, 0x46,
+       0x01, 0x42, 0x20, 0x31,
+       0x01, 0xbf, 0x84, 0x34,
+       0x01, 0x41, 0x7e, 0x31,
+       0x01, 0x90, 0x82, 0x34,
+       0x01, 0x65, 0x22, 0x31,
+       0xff, 0x6a, 0xd4, 0x08,
+       0xff, 0x6a, 0xd4, 0x0c
+};
+
+static int aic_patch23_func(struct ahc_softc *ahc);
+
+static int
+aic_patch23_func(struct ahc_softc *ahc)
+{
+       return ((ahc->bugs & AHC_SCBCHAN_UPLOAD_BUG) != 0);
+}
+
+static int aic_patch22_func(struct ahc_softc *ahc);
+
+static int
+aic_patch22_func(struct ahc_softc *ahc)
+{
+       return ((ahc->features & AHC_CMD_CHAN) == 0);
+}
+
+static int aic_patch21_func(struct ahc_softc *ahc);
+
+static int
+aic_patch21_func(struct ahc_softc *ahc)
+{
+       return ((ahc->features & AHC_QUEUE_REGS) == 0);
+}
+
+static int aic_patch20_func(struct ahc_softc *ahc);
+
+static int
+aic_patch20_func(struct ahc_softc *ahc)
+{
+       return ((ahc->features & AHC_WIDE) != 0);
+}
+
+static int aic_patch19_func(struct ahc_softc *ahc);
+
+static int
+aic_patch19_func(struct ahc_softc *ahc)
+{
+       return ((ahc->flags & AHC_SCB_BTT) != 0);
+}
+
+static int aic_patch18_func(struct ahc_softc *ahc);
+
+static int
+aic_patch18_func(struct ahc_softc *ahc)
+{
+       return ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0);
+}
+
+static int aic_patch17_func(struct ahc_softc *ahc);
+
+static int
+aic_patch17_func(struct ahc_softc *ahc)
+{
+       return ((ahc->flags & AHC_TMODE_WIDEODD_BUG) != 0);
+}
+
+static int aic_patch16_func(struct ahc_softc *ahc);
+
+static int
+aic_patch16_func(struct ahc_softc *ahc)
+{
+       return ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0);
+}
+
+static int aic_patch15_func(struct ahc_softc *ahc);
+
+static int
+aic_patch15_func(struct ahc_softc *ahc)
+{
+       return ((ahc->features & AHC_ULTRA2) == 0);
+}
+
+static int aic_patch14_func(struct ahc_softc *ahc);
+
+static int
+aic_patch14_func(struct ahc_softc *ahc)
+{
+       return ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0);
+}
+
+static int aic_patch13_func(struct ahc_softc *ahc);
+
+static int
+aic_patch13_func(struct ahc_softc *ahc)
+{
+       return ((ahc->flags & AHC_39BIT_ADDRESSING) != 0);
+}
+
+static int aic_patch12_func(struct ahc_softc *ahc);
+
+static int
+aic_patch12_func(struct ahc_softc *ahc)
+{
+       return ((ahc->features & AHC_HS_MAILBOX) != 0);
+}
+
+static int aic_patch11_func(struct ahc_softc *ahc);
+
+static int
+aic_patch11_func(struct ahc_softc *ahc)
+{
+       return ((ahc->features & AHC_ULTRA) != 0);
+}
+
+static int aic_patch10_func(struct ahc_softc *ahc);
+
+static int
+aic_patch10_func(struct ahc_softc *ahc)
+{
+       return ((ahc->features & AHC_MULTI_TID) != 0);
+}
+
+static int aic_patch9_func(struct ahc_softc *ahc);
+
+static int
+aic_patch9_func(struct ahc_softc *ahc)
+{
+       return ((ahc->features & AHC_CMD_CHAN) != 0);
+}
+
+static int aic_patch8_func(struct ahc_softc *ahc);
+
+static int
+aic_patch8_func(struct ahc_softc *ahc)
+{
+       return ((ahc->flags & AHC_INITIATORROLE) != 0);
+}
+
+static int aic_patch7_func(struct ahc_softc *ahc);
+
+static int
+aic_patch7_func(struct ahc_softc *ahc)
+{
+       return ((ahc->flags & AHC_TARGETROLE) != 0);
+}
+
+static int aic_patch6_func(struct ahc_softc *ahc);
+
+static int
+aic_patch6_func(struct ahc_softc *ahc)
+{
+       return ((ahc->features & AHC_DT) == 0);
+}
+
+static int aic_patch5_func(struct ahc_softc *ahc);
+
+static int
+aic_patch5_func(struct ahc_softc *ahc)
+{
+       return ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0);
+}
+
+static int aic_patch4_func(struct ahc_softc *ahc);
+
+static int
+aic_patch4_func(struct ahc_softc *ahc)
+{
+       return ((ahc->flags & AHC_PAGESCBS) != 0);
+}
+
+static int aic_patch3_func(struct ahc_softc *ahc);
+
+static int
+aic_patch3_func(struct ahc_softc *ahc)
+{
+       return ((ahc->features & AHC_QUEUE_REGS) != 0);
+}
+
+static int aic_patch2_func(struct ahc_softc *ahc);
+
+static int
+aic_patch2_func(struct ahc_softc *ahc)
+{
+       return ((ahc->features & AHC_TWIN) != 0);
+}
+
+static int aic_patch1_func(struct ahc_softc *ahc);
+
+static int
+aic_patch1_func(struct ahc_softc *ahc)
+{
+       return ((ahc->features & AHC_ULTRA2) != 0);
+}
+
+static int aic_patch0_func(struct ahc_softc *ahc);
+
+static int
+aic_patch0_func(struct ahc_softc *ahc)
+{
+       return (0);
+}
+
+typedef int patch_func_t (struct ahc_softc *ahc);
+static struct patch {
+       patch_func_t    *patch_func;
+       uint32_t        begin      :10,
+                       skip_instr :10,
+                       skip_patch :12;
+} patches[] = {
+       { aic_patch1_func, 4, 1, 1 },
+       { aic_patch2_func, 6, 2, 1 },
+       { aic_patch2_func, 9, 1, 1 },
+       { aic_patch3_func, 11, 1, 2 },
+       { aic_patch0_func, 12, 2, 1 },
+       { aic_patch4_func, 15, 1, 2 },
+       { aic_patch0_func, 16, 1, 1 },
+       { aic_patch5_func, 22, 2, 1 },
+       { aic_patch3_func, 27, 1, 2 },
+       { aic_patch0_func, 28, 1, 1 },
+       { aic_patch6_func, 34, 1, 1 },
+       { aic_patch7_func, 37, 54, 19 },
+       { aic_patch8_func, 37, 1, 1 },
+       { aic_patch9_func, 42, 3, 2 },
+       { aic_patch0_func, 45, 3, 1 },
+       { aic_patch10_func, 49, 1, 2 },
+       { aic_patch0_func, 50, 2, 3 },
+       { aic_patch1_func, 50, 1, 2 },
+       { aic_patch0_func, 51, 1, 1 },
+       { aic_patch2_func, 53, 2, 1 },
+       { aic_patch9_func, 55, 1, 2 },
+       { aic_patch0_func, 56, 1, 1 },
+       { aic_patch9_func, 60, 1, 2 },
+       { aic_patch0_func, 61, 1, 1 },
+       { aic_patch9_func, 71, 1, 2 },
+       { aic_patch0_func, 72, 1, 1 },
+       { aic_patch9_func, 75, 1, 2 },
+       { aic_patch0_func, 76, 1, 1 },
+       { aic_patch9_func, 79, 1, 2 },
+       { aic_patch0_func, 80, 1, 1 },
+       { aic_patch8_func, 91, 9, 4 },
+       { aic_patch1_func, 93, 1, 2 },
+       { aic_patch0_func, 94, 1, 1 },
+       { aic_patch2_func, 96, 2, 1 },
+       { aic_patch2_func, 105, 4, 1 },
+       { aic_patch1_func, 109, 1, 2 },
+       { aic_patch0_func, 110, 2, 3 },
+       { aic_patch2_func, 110, 1, 2 },
+       { aic_patch0_func, 111, 1, 1 },
+       { aic_patch7_func, 112, 4, 2 },
+       { aic_patch0_func, 116, 1, 1 },
+       { aic_patch11_func, 118, 2, 1 },
+       { aic_patch1_func, 120, 1, 2 },
+       { aic_patch0_func, 121, 1, 1 },
+       { aic_patch7_func, 122, 4, 1 },
+       { aic_patch7_func, 132, 91, 11 },
+       { aic_patch4_func, 151, 1, 1 },
+       { aic_patch1_func, 165, 1, 1 },
+       { aic_patch12_func, 170, 1, 2 },
+       { aic_patch0_func, 171, 1, 1 },
+       { aic_patch9_func, 182, 1, 2 },
+       { aic_patch0_func, 183, 1, 1 },
+       { aic_patch9_func, 192, 1, 2 },
+       { aic_patch0_func, 193, 1, 1 },
+       { aic_patch9_func, 209, 6, 2 },
+       { aic_patch0_func, 215, 6, 1 },
+       { aic_patch8_func, 223, 19, 2 },
+       { aic_patch1_func, 237, 1, 1 },
+       { aic_patch1_func, 244, 1, 2 },
+       { aic_patch0_func, 245, 2, 2 },
+       { aic_patch11_func, 246, 1, 1 },
+       { aic_patch9_func, 254, 31, 3 },
+       { aic_patch1_func, 270, 14, 2 },
+       { aic_patch13_func, 275, 1, 1 },
+       { aic_patch14_func, 285, 14, 1 },
+       { aic_patch1_func, 301, 1, 2 },
+       { aic_patch0_func, 302, 1, 1 },
+       { aic_patch9_func, 305, 1, 1 },
+       { aic_patch13_func, 310, 1, 1 },
+       { aic_patch9_func, 311, 2, 2 },
+       { aic_patch0_func, 313, 4, 1 },
+       { aic_patch14_func, 317, 1, 1 },
+       { aic_patch15_func, 320, 2, 3 },
+       { aic_patch9_func, 320, 1, 2 },
+       { aic_patch0_func, 321, 1, 1 },
+       { aic_patch6_func, 326, 1, 2 },
+       { aic_patch0_func, 327, 1, 1 },
+       { aic_patch1_func, 331, 50, 11 },
+       { aic_patch6_func, 340, 2, 4 },
+       { aic_patch7_func, 340, 1, 1 },
+       { aic_patch8_func, 341, 1, 1 },
+       { aic_patch0_func, 342, 1, 1 },
+       { aic_patch16_func, 343, 1, 1 },
+       { aic_patch6_func, 362, 6, 3 },
+       { aic_patch16_func, 362, 5, 1 },
+       { aic_patch0_func, 368, 5, 1 },
+       { aic_patch13_func, 376, 5, 1 },
+       { aic_patch0_func, 381, 54, 17 },
+       { aic_patch14_func, 381, 1, 1 },
+       { aic_patch7_func, 383, 2, 2 },
+       { aic_patch17_func, 384, 1, 1 },
+       { aic_patch9_func, 387, 1, 1 },
+       { aic_patch18_func, 394, 1, 1 },
+       { aic_patch14_func, 399, 9, 3 },
+       { aic_patch9_func, 400, 3, 2 },
+       { aic_patch0_func, 403, 3, 1 },
+       { aic_patch9_func, 411, 6, 2 },
+       { aic_patch0_func, 417, 9, 2 },
+       { aic_patch13_func, 417, 1, 1 },
+       { aic_patch13_func, 426, 2, 1 },
+       { aic_patch14_func, 428, 1, 1 },
+       { aic_patch9_func, 430, 1, 2 },
+       { aic_patch0_func, 431, 1, 1 },
+       { aic_patch7_func, 434, 1, 1 },
+       { aic_patch7_func, 435, 1, 1 },
+       { aic_patch8_func, 436, 3, 3 },
+       { aic_patch6_func, 437, 1, 2 },
+       { aic_patch0_func, 438, 1, 1 },
+       { aic_patch9_func, 439, 1, 1 },
+       { aic_patch15_func, 440, 1, 2 },
+       { aic_patch13_func, 440, 1, 1 },
+       { aic_patch14_func, 442, 9, 4 },
+       { aic_patch9_func, 442, 1, 1 },
+       { aic_patch9_func, 449, 2, 1 },
+       { aic_patch0_func, 451, 4, 3 },
+       { aic_patch9_func, 451, 1, 2 },
+       { aic_patch0_func, 452, 3, 1 },
+       { aic_patch1_func, 456, 2, 1 },
+       { aic_patch7_func, 458, 10, 2 },
+       { aic_patch0_func, 468, 1, 1 },
+       { aic_patch8_func, 469, 109, 23 },
+       { aic_patch1_func, 471, 3, 2 },
+       { aic_patch0_func, 474, 5, 3 },
+       { aic_patch9_func, 474, 2, 2 },
+       { aic_patch0_func, 476, 3, 1 },
+       { aic_patch1_func, 481, 2, 2 },
+       { aic_patch0_func, 483, 6, 3 },
+       { aic_patch9_func, 483, 2, 2 },
+       { aic_patch0_func, 485, 3, 1 },
+       { aic_patch1_func, 491, 2, 2 },
+       { aic_patch0_func, 493, 9, 7 },
+       { aic_patch9_func, 493, 5, 6 },
+       { aic_patch19_func, 493, 1, 2 },
+       { aic_patch0_func, 494, 1, 1 },
+       { aic_patch19_func, 496, 1, 2 },
+       { aic_patch0_func, 497, 1, 1 },
+       { aic_patch0_func, 498, 4, 1 },
+       { aic_patch6_func, 502, 3, 2 },
+       { aic_patch0_func, 505, 1, 1 },
+       { aic_patch1_func, 508, 1, 1 },
+       { aic_patch6_func, 514, 1, 2 },
+       { aic_patch0_func, 515, 1, 1 },
+       { aic_patch20_func, 552, 7, 1 },
+       { aic_patch3_func, 580, 1, 2 },
+       { aic_patch0_func, 581, 1, 1 },
+       { aic_patch21_func, 584, 1, 1 },
+       { aic_patch8_func, 586, 104, 33 },
+       { aic_patch4_func, 587, 1, 1 },
+       { aic_patch1_func, 593, 2, 2 },
+       { aic_patch0_func, 595, 1, 1 },
+       { aic_patch1_func, 598, 1, 2 },
+       { aic_patch0_func, 599, 1, 1 },
+       { aic_patch9_func, 600, 3, 3 },
+       { aic_patch15_func, 601, 1, 1 },
+       { aic_patch0_func, 603, 4, 1 },
+       { aic_patch19_func, 611, 2, 2 },
+       { aic_patch0_func, 613, 1, 1 },
+       { aic_patch19_func, 617, 10, 3 },
+       { aic_patch5_func, 619, 8, 1 },
+       { aic_patch0_func, 627, 9, 2 },
+       { aic_patch5_func, 628, 8, 1 },
+       { aic_patch4_func, 638, 1, 2 },
+       { aic_patch0_func, 639, 1, 1 },
+       { aic_patch19_func, 640, 1, 2 },
+       { aic_patch0_func, 641, 3, 2 },
+       { aic_patch4_func, 643, 1, 1 },
+       { aic_patch5_func, 644, 1, 1 },
+       { aic_patch5_func, 647, 1, 1 },
+       { aic_patch5_func, 649, 1, 1 },
+       { aic_patch4_func, 651, 2, 2 },
+       { aic_patch0_func, 653, 2, 1 },
+       { aic_patch5_func, 655, 1, 1 },
+       { aic_patch5_func, 658, 1, 1 },
+       { aic_patch5_func, 661, 1, 1 },
+       { aic_patch19_func, 665, 1, 1 },
+       { aic_patch19_func, 668, 1, 1 },
+       { aic_patch4_func, 674, 1, 1 },
+       { aic_patch6_func, 677, 1, 2 },
+       { aic_patch0_func, 678, 1, 1 },
+       { aic_patch7_func, 690, 16, 1 },
+       { aic_patch4_func, 706, 20, 1 },
+       { aic_patch9_func, 727, 4, 2 },
+       { aic_patch0_func, 731, 4, 1 },
+       { aic_patch9_func, 735, 4, 2 },
+       { aic_patch0_func, 739, 3, 1 },
+       { aic_patch6_func, 745, 1, 1 },
+       { aic_patch22_func, 747, 14, 1 },
+       { aic_patch7_func, 761, 3, 1 },
+       { aic_patch9_func, 773, 24, 8 },
+       { aic_patch19_func, 777, 1, 2 },
+       { aic_patch0_func, 778, 1, 1 },
+       { aic_patch15_func, 783, 4, 2 },
+       { aic_patch0_func, 787, 7, 3 },
+       { aic_patch23_func, 787, 5, 2 },
+       { aic_patch0_func, 792, 2, 1 },
+       { aic_patch0_func, 797, 42, 3 },
+       { aic_patch18_func, 809, 18, 2 },
+       { aic_patch0_func, 827, 1, 1 },
+       { aic_patch4_func, 851, 1, 1 },
+       { aic_patch4_func, 852, 3, 2 },
+       { aic_patch0_func, 855, 1, 1 },
+       { aic_patch13_func, 856, 3, 1 },
+       { aic_patch4_func, 859, 12, 1 }
+};
+static struct cs {
+       u_int16_t       begin;
+       u_int16_t       end;
+} critical_sections[] = {
+       { 11, 18 },
+       { 21, 30 },
+       { 706, 722 },
+       { 852, 855 },
+       { 859, 865 },
+       { 867, 869 },
+       { 869, 871 }
+};
+static const int num_critical_sections = sizeof(critical_sections)
+                                      / sizeof(*critical_sections);
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/Makefile b/xen/drivers/scsi/aic7xxx/aicasm/Makefile
new file mode 100644 (file)
index 0000000..16394e1
--- /dev/null
@@ -0,0 +1,65 @@
+PROG=  aicasm
+
+.SUFFIXES= .l .y .c .h
+
+CSRCS= aicasm.c aicasm_symbol.c
+YSRCS= aicasm_gram.y aicasm_macro_gram.y
+LSRCS= aicasm_scan.l aicasm_macro_scan.l
+
+GENHDRS=       aicdb.h $(YSRCS:.y=.h)
+GENSRCS=       $(YSRCS:.y=.c) $(LSRCS:.l=.c)
+
+SRCS=  ${CSRCS} ${GENSRCS}
+CLEANFILES= ${GENSRCS} ${GENHDRS} $(YSRCS:.y=.output)
+# Override default kernel CFLAGS.  This is a userland app.
+AICASM_CFLAGS:= -I/usr/include -I. -ldb
+YFLAGS= -d
+
+NOMAN= noman
+
+ifneq ($(HOSTCC),)
+AICASM_CC= $(HOSTCC)
+else
+AICASM_CC= $(CC)
+endif
+
+ifdef DEBUG
+CFLAGS+= -DDEBUG -g
+YFLAGS+= -t -v
+LFLAGS= -d
+endif
+
+$(PROG):  ${GENHDRS} $(SRCS)
+       $(AICASM_CC) $(AICASM_CFLAGS) $(SRCS) -o $(PROG)
+
+aicdb.h:
+       @if [ -e "/usr/include/db3/db_185.h" ]; then            \
+               echo "#include <db3/db_185.h>" > aicdb.h;       \
+        elif [ -e "/usr/include/db2/db_185.h" ]; then          \
+               echo "#include <db2/db_185.h>" > aicdb.h;       \
+        elif [ -e "/usr/include/db/db_185.h" ]; then           \
+               echo "#include <db/db_185.h>" > aicdb.h;        \
+        elif [ -e "/usr/include/db_185.h" ]; then              \
+               echo "#include <db_185.h>" > aicdb.h;           \
+        else                                                   \
+               echo "*** Install db development libraries";    \
+        fi
+
+clean:
+       rm -f $(CLEANFILES) $(PROG)
+
+aicasm_gram.c aicasm_gram.h: aicasm_gram.y
+       $(YACC) $(YFLAGS) -b $(<:.y=) $<
+       mv $(<:.y=).tab.c $(<:.y=.c)
+       mv $(<:.y=).tab.h $(<:.y=.h)
+
+aicasm_macro_gram.c aicasm_macro_gram.h: aicasm_macro_gram.y
+       $(YACC) $(YFLAGS) -b $(<:.y=) -p mm $<
+       mv $(<:.y=).tab.c $(<:.y=.c)
+       mv $(<:.y=).tab.h $(<:.y=.h)
+
+aicasm_scan.c: aicasm_scan.l
+       $(LEX) $(LFLAGS) -o$@ $<
+
+aicasm_macro_scan.c: aicasm_macro_scan.l
+       $(LEX) $(LFLAGS) -Pmm -o$@ $<
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm.c b/xen/drivers/scsi/aic7xxx/aicasm/aicasm.c
new file mode 100644 (file)
index 0000000..bc93c3d
--- /dev/null
@@ -0,0 +1,809 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler
+ *
+ * Copyright (c) 1997, 1998, 2000, 2001 Justin T. Gibbs.
+ * Copyright (c) 2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm.c#16 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm.c,v 1.28.2.5 2002/04/29 19:36:36 gibbs Exp $
+ */
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#if linux
+#include <endian.h>
+#else
+#include <machine/endian.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+typedef struct patch {
+       STAILQ_ENTRY(patch) links;
+       int             patch_func;
+       u_int           begin;
+       u_int           skip_instr;
+       u_int           skip_patch;
+} patch_t;
+
+STAILQ_HEAD(patch_list, patch) patches;
+
+static void usage(void);
+static void back_patch(void);
+static void output_code(void);
+static void output_listing(char *ifilename);
+static void dump_scope(scope_t *scope);
+static void emit_patch(scope_t *scope, int patch);
+static int check_patch(patch_t **start_patch, int start_instr,
+                      int *skip_addr, int *func_vals);
+
+struct path_list search_path;
+int includes_search_curdir;
+char *appname;
+FILE *ofile;
+char *ofilename;
+char *regfilename;
+FILE *regfile;
+char *listfilename;
+FILE *listfile;
+int   src_mode;
+int   dst_mode;
+
+static STAILQ_HEAD(,instruction) seq_program;
+struct cs_tailq cs_tailq;
+struct scope_list scope_stack;
+symlist_t patch_functions;
+
+#if DEBUG
+extern int yy_flex_debug;
+extern int mm_flex_debug;
+extern int yydebug;
+extern int mmdebug;
+#endif
+extern FILE *yyin;
+extern int yyparse(void);
+
+int main(int argc, char *argv[]);
+
+int
+main(int argc, char *argv[])
+{
+       extern char *optarg;
+       extern int optind;
+       int  ch;
+       int  retval;
+       char *inputfilename;
+       scope_t *sentinal;
+
+       STAILQ_INIT(&patches);
+       SLIST_INIT(&search_path);
+       STAILQ_INIT(&seq_program);
+       TAILQ_INIT(&cs_tailq);
+       SLIST_INIT(&scope_stack);
+
+       /* Set Sentinal scope node */
+       sentinal = scope_alloc();
+       sentinal->type = SCOPE_ROOT;
+       
+       includes_search_curdir = 1;
+       appname = *argv;
+       regfile = NULL;
+       listfile = NULL;
+#if DEBUG
+       yy_flex_debug = 0;
+       mm_flex_debug = 0;
+       yydebug = 0;
+       mmdebug = 0;
+#endif
+       while ((ch = getopt(argc, argv, "d:l:n:o:r:I:O:")) != -1) {
+               switch(ch) {
+               case 'd':
+#if DEBUG
+                       if (strcmp(optarg, "s") == 0) {
+                               yy_flex_debug = 1;
+                               mm_flex_debug = 1;
+                       } else if (strcmp(optarg, "p") == 0) {
+                               yydebug = 1;
+                               mmdebug = 1;
+                       } else {
+                               fprintf(stderr, "%s: -d Requires either an "
+                                       "'s' or 'p' argument\n", appname);
+                               usage();
+                       }
+#else
+                       stop("-d: Assembler not built with debugging "
+                            "information", EX_SOFTWARE);
+#endif
+                       break;
+               case 'l':
+                       /* Create a program listing */
+                       if ((listfile = fopen(optarg, "w")) == NULL) {
+                               perror(optarg);
+                               stop(NULL, EX_CANTCREAT);
+                       }
+                       listfilename = optarg;
+                       break;
+               case 'n':
+                       /* Don't complain about the -nostdinc directrive */
+                       if (strcmp(optarg, "ostdinc")) {
+                               fprintf(stderr, "%s: Unknown option -%c%s\n",
+                                       appname, ch, optarg);
+                               usage();
+                               /* NOTREACHED */
+                       }
+                       break;
+               case 'o':
+                       if ((ofile = fopen(optarg, "w")) == NULL) {
+                               perror(optarg);
+                               stop(NULL, EX_CANTCREAT);
+                       }
+                       ofilename = optarg;
+                       break;
+               case 'r':
+                       if ((regfile = fopen(optarg, "w")) == NULL) {
+                               perror(optarg);
+                               stop(NULL, EX_CANTCREAT);
+                       }
+                       regfilename = optarg;
+                       break;
+               case 'I':
+               {
+                       path_entry_t include_dir;
+
+                       if (strcmp(optarg, "-") == 0) {
+                               if (includes_search_curdir == 0) {
+                                       fprintf(stderr, "%s: Warning - '-I-' "
+                                                       "specified multiple "
+                                                       "times\n", appname);
+                               }
+                               includes_search_curdir = 0;
+                               for (include_dir = SLIST_FIRST(&search_path);
+                                    include_dir != NULL;
+                                    include_dir = SLIST_NEXT(include_dir,
+                                                             links))
+                                       /*
+                                        * All entries before a '-I-' only
+                                        * apply to includes specified with
+                                        * quotes instead of "<>".
+                                        */
+                                       include_dir->quoted_includes_only = 1;
+                       } else {
+                               include_dir =
+                                   (path_entry_t)malloc(sizeof(*include_dir));
+                               if (include_dir == NULL) {
+                                       perror(optarg);
+                                       stop(NULL, EX_OSERR);
+                               }
+                               include_dir->directory = strdup(optarg);
+                               if (include_dir->directory == NULL) {
+                                       perror(optarg);
+                                       stop(NULL, EX_OSERR);
+                               }
+                               include_dir->quoted_includes_only = 0;
+                               SLIST_INSERT_HEAD(&search_path, include_dir,
+                                                 links);
+                       }
+                       break;
+               }
+               case '?':
+               default:
+                       usage();
+                       /* NOTREACHED */
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (argc != 1) {
+               fprintf(stderr, "%s: No input file specifiled\n", appname);
+               usage();
+               /* NOTREACHED */
+       }
+
+       symtable_open();
+       inputfilename = *argv;
+       include_file(*argv, SOURCE_FILE);
+       retval = yyparse();
+       if (retval == 0) {
+               if (SLIST_FIRST(&scope_stack) == NULL
+                || SLIST_FIRST(&scope_stack)->type != SCOPE_ROOT) {
+                       stop("Unterminated conditional expression", EX_DATAERR);
+                       /* NOTREACHED */
+               }
+
+               /* Process outmost scope */
+               process_scope(SLIST_FIRST(&scope_stack));
+               /*
+                * Decend the tree of scopes and insert/emit
+                * patches as appropriate.  We perform a depth first
+                * tranversal, recursively handling each scope.
+                */
+               /* start at the root scope */
+               dump_scope(SLIST_FIRST(&scope_stack));
+
+               /* Patch up forward jump addresses */
+               back_patch();
+
+               if (ofile != NULL)
+                       output_code();
+               if (regfile != NULL) {
+                       symtable_dump(regfile);
+               }
+               if (listfile != NULL)
+                       output_listing(inputfilename);
+       }
+
+       stop(NULL, 0);
+       /* NOTREACHED */
+       return (0);
+}
+
+static void
+usage()
+{
+
+       (void)fprintf(stderr,
+"usage: %-16s [-nostdinc] [-I-] [-I directory] [-o output_file]
+                       [-r register_output_file] [-l program_list_file]
+                       input_file\n",
+                       appname);
+       exit(EX_USAGE);
+}
+
+static void
+back_patch()
+{
+       struct instruction *cur_instr;
+
+       for (cur_instr = STAILQ_FIRST(&seq_program);
+            cur_instr != NULL;
+            cur_instr = STAILQ_NEXT(cur_instr, links)) {
+               if (cur_instr->patch_label != NULL) {
+                       struct ins_format3 *f3_instr;
+                       u_int address;
+
+                       if (cur_instr->patch_label->type != LABEL) {
+                               char buf[255];
+
+                               snprintf(buf, sizeof(buf),
+                                        "Undefined label %s",
+                                        cur_instr->patch_label->name);
+                               stop(buf, EX_DATAERR);
+                               /* NOTREACHED */
+                       }
+                       f3_instr = &cur_instr->format.format3;
+                       address = f3_instr->address;
+                       address += cur_instr->patch_label->info.linfo->address;
+                       f3_instr->address = address;
+               }
+       }
+}
+
+static void
+output_code()
+{
+       struct instruction *cur_instr;
+       patch_t *cur_patch;
+       critical_section_t *cs;
+       symbol_node_t *cur_node;
+       int instrcount;
+
+       instrcount = 0;
+       fprintf(ofile,
+"/*
+ * DO NOT EDIT - This file is automatically generated
+ *              from the following source files:
+ *
+%s */\n", versions);
+
+       fprintf(ofile, "static uint8_t seqprog[] = {\n");
+       for (cur_instr = STAILQ_FIRST(&seq_program);
+            cur_instr != NULL;
+            cur_instr = STAILQ_NEXT(cur_instr, links)) {
+
+               fprintf(ofile, "%s\t0x%02x, 0x%02x, 0x%02x, 0x%02x",
+                       cur_instr == STAILQ_FIRST(&seq_program) ? "" : ",\n",
+#if BYTE_ORDER == LITTLE_ENDIAN
+                       cur_instr->format.bytes[0],
+                       cur_instr->format.bytes[1],
+                       cur_instr->format.bytes[2],
+                       cur_instr->format.bytes[3]);
+#else
+                       cur_instr->format.bytes[3],
+                       cur_instr->format.bytes[2],
+                       cur_instr->format.bytes[1],
+                       cur_instr->format.bytes[0]);
+#endif
+               instrcount++;
+       }
+       fprintf(ofile, "\n};\n\n");
+
+       if (patch_arg_list == NULL)
+               stop("Patch argument list not defined",
+                    EX_DATAERR);
+
+       /*
+        *  Output patch information.  Patch functions first.
+        */
+       for (cur_node = SLIST_FIRST(&patch_functions);
+            cur_node != NULL;
+            cur_node = SLIST_NEXT(cur_node,links)) {
+               fprintf(ofile,
+"static int aic_patch%d_func(%s);
+
+static int
+aic_patch%d_func(%s)
+{
+       return (%s);
+}\n\n",
+                       cur_node->symbol->info.condinfo->func_num,
+                       patch_arg_list,
+                       cur_node->symbol->info.condinfo->func_num,
+                       patch_arg_list,
+                       cur_node->symbol->name);
+       }
+
+       fprintf(ofile,
+"typedef int patch_func_t (%s);
+static struct patch {
+       patch_func_t    *patch_func;
+       uint32_t        begin      :10,
+                       skip_instr :10,
+                       skip_patch :12;
+} patches[] = {\n", patch_arg_list);
+
+       for (cur_patch = STAILQ_FIRST(&patches);
+            cur_patch != NULL;
+            cur_patch = STAILQ_NEXT(cur_patch,links)) {
+               fprintf(ofile, "%s\t{ aic_patch%d_func, %d, %d, %d }",
+                       cur_patch == STAILQ_FIRST(&patches) ? "" : ",\n",
+                       cur_patch->patch_func, cur_patch->begin,
+                       cur_patch->skip_instr, cur_patch->skip_patch);
+       }
+
+       fprintf(ofile, "\n};\n");
+
+       fprintf(ofile,
+"static struct cs {
+       u_int16_t       begin;
+       u_int16_t       end;
+} critical_sections[] = {\n");
+
+       for (cs = TAILQ_FIRST(&cs_tailq);
+            cs != NULL;
+            cs = TAILQ_NEXT(cs, links)) {
+               fprintf(ofile, "%s\t{ %d, %d }",
+                       cs == TAILQ_FIRST(&cs_tailq) ? "" : ",\n",
+                       cs->begin_addr, cs->end_addr);
+       }
+
+       fprintf(ofile, "\n};\n");
+
+       fprintf(ofile,
+"static const int num_critical_sections = sizeof(critical_sections)
+                                      / sizeof(*critical_sections);\n");
+
+       fprintf(stderr, "%s: %d instructions used\n", appname, instrcount);
+}
+
+static void
+dump_scope(scope_t *scope)
+{
+       scope_t *cur_scope;
+
+       /*
+        * Emit the first patch for this scope
+        */
+       emit_patch(scope, 0);
+
+       /*
+        * Dump each scope within this one.
+        */
+       cur_scope = TAILQ_FIRST(&scope->inner_scope);
+
+       while (cur_scope != NULL) {
+
+               dump_scope(cur_scope);
+
+               cur_scope = TAILQ_NEXT(cur_scope, scope_links);
+       }
+
+       /*
+        * Emit the second, closing, patch for this scope
+        */
+       emit_patch(scope, 1);
+}
+
+void
+emit_patch(scope_t *scope, int patch)
+{
+       patch_info_t *pinfo;
+       patch_t *new_patch;
+
+       pinfo = &scope->patches[patch];
+
+       if (pinfo->skip_instr == 0)
+               /* No-Op patch */
+               return;
+
+       new_patch = (patch_t *)malloc(sizeof(*new_patch));
+
+       if (new_patch == NULL)
+               stop("Could not malloc patch structure", EX_OSERR);
+
+       memset(new_patch, 0, sizeof(*new_patch));
+
+       if (patch == 0) {
+               new_patch->patch_func = scope->func_num;
+               new_patch->begin = scope->begin_addr;
+       } else {
+               new_patch->patch_func = 0;
+               new_patch->begin = scope->end_addr;
+       }
+       new_patch->skip_instr = pinfo->skip_instr;
+       new_patch->skip_patch = pinfo->skip_patch;
+       STAILQ_INSERT_TAIL(&patches, new_patch, links);
+}
+
+void
+output_listing(char *ifilename)
+{
+       char buf[1024];
+       FILE *ifile;
+       struct instruction *cur_instr;
+       patch_t *cur_patch;
+       symbol_node_t *cur_func;
+       int *func_values;
+       int instrcount;
+       int instrptr;
+       int line;
+       int func_count;
+       int skip_addr;
+
+       instrcount = 0;
+       instrptr = 0;
+       line = 1;
+       skip_addr = 0;
+       if ((ifile = fopen(ifilename, "r")) == NULL) {
+               perror(ifilename);
+               stop(NULL, EX_DATAERR);
+       }
+
+       /*
+        * Determine which options to apply to this listing.
+        */
+       for (func_count = 0, cur_func = SLIST_FIRST(&patch_functions);
+           cur_func != NULL;
+           cur_func = SLIST_NEXT(cur_func, links))
+               func_count++;
+
+       func_values = NULL;
+       if (func_count != 0) {
+               func_values = (int *)malloc(func_count * sizeof(int));
+
+               if (func_values == NULL)
+                       stop("Could not malloc", EX_OSERR);
+               
+               func_values[0] = 0; /* FALSE func */
+               func_count--;
+
+               /*
+                * Ask the user to fill in the return values for
+                * the rest of the functions.
+                */
+               
+               
+               for (cur_func = SLIST_FIRST(&patch_functions);
+                    cur_func != NULL && SLIST_NEXT(cur_func, links) != NULL;
+                    cur_func = SLIST_NEXT(cur_func, links), func_count--) {
+                       int input;
+                       
+                       fprintf(stdout, "\n(%s)\n", cur_func->symbol->name);
+                       fprintf(stdout,
+                               "Enter the return value for "
+                               "this expression[T/F]:");
+
+                       while (1) {
+
+                               input = getchar();
+                               input = toupper(input);
+
+                               if (input == 'T') {
+                                       func_values[func_count] = 1;
+                                       break;
+                               } else if (input == 'F') {
+                                       func_values[func_count] = 0;
+                                       break;
+                               }
+                       }
+                       if (isatty(fileno(stdin)) == 0)
+                               putchar(input);
+               }
+               fprintf(stdout, "\nThanks!\n");
+       }
+
+       /* Now output the listing */
+       cur_patch = STAILQ_FIRST(&patches);
+       for (cur_instr = STAILQ_FIRST(&seq_program);
+            cur_instr != NULL;
+            cur_instr = STAILQ_NEXT(cur_instr, links), instrcount++) {
+
+               if (check_patch(&cur_patch, instrcount,
+                               &skip_addr, func_values) == 0) {
+                       /* Don't count this instruction as it is in a patch
+                        * that was removed.
+                        */
+                        continue;
+               }
+
+               while (line < cur_instr->srcline) {
+                       fgets(buf, sizeof(buf), ifile);
+                               fprintf(listfile, "\t\t%s", buf);
+                               line++;
+               }
+               fprintf(listfile, "%03x %02x%02x%02x%02x", instrptr,
+#if BYTE_ORDER == LITTLE_ENDIAN
+                       cur_instr->format.bytes[0],
+                       cur_instr->format.bytes[1],
+                       cur_instr->format.bytes[2],
+                       cur_instr->format.bytes[3]);
+#else
+                       cur_instr->format.bytes[3],
+                       cur_instr->format.bytes[2],
+                       cur_instr->format.bytes[1],
+                       cur_instr->format.bytes[0]);
+#endif
+               fgets(buf, sizeof(buf), ifile);
+               fprintf(listfile, "\t%s", buf);
+               line++;
+               instrptr++;
+       }
+       /* Dump the remainder of the file */
+       while(fgets(buf, sizeof(buf), ifile) != NULL)
+               fprintf(listfile, "\t\t%s", buf);
+
+       fclose(ifile);
+}
+
+static int
+check_patch(patch_t **start_patch, int start_instr,
+           int *skip_addr, int *func_vals)
+{
+       patch_t *cur_patch;
+
+       cur_patch = *start_patch;
+
+       while (cur_patch != NULL && start_instr == cur_patch->begin) {
+               if (func_vals[cur_patch->patch_func] == 0) {
+                       int skip;
+
+                       /* Start rejecting code */
+                       *skip_addr = start_instr + cur_patch->skip_instr;
+                       for (skip = cur_patch->skip_patch;
+                            skip > 0 && cur_patch != NULL;
+                            skip--)
+                               cur_patch = STAILQ_NEXT(cur_patch, links);
+               } else {
+                       /* Accepted this patch.  Advance to the next
+                        * one and wait for our intruction pointer to
+                        * hit this point.
+                        */
+                       cur_patch = STAILQ_NEXT(cur_patch, links);
+               }
+       }
+
+       *start_patch = cur_patch;
+       if (start_instr < *skip_addr)
+               /* Still skipping */
+               return (0);
+
+       return (1);
+}
+
+/*
+ * Print out error information if appropriate, and clean up before
+ * terminating the program.
+ */
+void
+stop(const char *string, int err_code)
+{
+       if (string != NULL) {
+               fprintf(stderr, "%s: ", appname);
+               if (yyfilename != NULL) {
+                       fprintf(stderr, "Stopped at file %s, line %d - ",
+                               yyfilename, yylineno);
+               }
+               fprintf(stderr, "%s\n", string);
+       }
+
+       if (ofile != NULL) {
+               fclose(ofile);
+               if (err_code != 0) {
+                       fprintf(stderr, "%s: Removing %s due to error\n",
+                               appname, ofilename);
+                       unlink(ofilename);
+               }
+       }
+
+       if (regfile != NULL) {
+               fclose(regfile);
+               if (err_code != 0) {
+                       fprintf(stderr, "%s: Removing %s due to error\n",
+                               appname, regfilename);
+                       unlink(regfilename);
+               }
+       }
+
+       if (listfile != NULL) {
+               fclose(listfile);
+               if (err_code != 0) {
+                       fprintf(stderr, "%s: Removing %s due to error\n",
+                               appname, listfilename);
+                       unlink(listfilename);
+               }
+       }
+
+       symlist_free(&patch_functions);
+       symtable_close();
+
+       exit(err_code);
+}
+
+struct instruction *
+seq_alloc()
+{
+       struct instruction *new_instr;
+
+       new_instr = (struct instruction *)malloc(sizeof(struct instruction));
+       if (new_instr == NULL)
+               stop("Unable to malloc instruction object", EX_SOFTWARE);
+       memset(new_instr, 0, sizeof(*new_instr));
+       STAILQ_INSERT_TAIL(&seq_program, new_instr, links);
+       new_instr->srcline = yylineno;
+       return new_instr;
+}
+
+critical_section_t *
+cs_alloc()
+{
+       critical_section_t *new_cs;
+
+       new_cs= (critical_section_t *)malloc(sizeof(critical_section_t));
+       if (new_cs == NULL)
+               stop("Unable to malloc critical_section object", EX_SOFTWARE);
+       memset(new_cs, 0, sizeof(*new_cs));
+       
+       TAILQ_INSERT_TAIL(&cs_tailq, new_cs, links);
+       return new_cs;
+}
+
+scope_t *
+scope_alloc()
+{
+       scope_t *new_scope;
+
+       new_scope = (scope_t *)malloc(sizeof(scope_t));
+       if (new_scope == NULL)
+               stop("Unable to malloc scope object", EX_SOFTWARE);
+       memset(new_scope, 0, sizeof(*new_scope));
+       TAILQ_INIT(&new_scope->inner_scope);
+       
+       if (SLIST_FIRST(&scope_stack) != NULL) {
+               TAILQ_INSERT_TAIL(&SLIST_FIRST(&scope_stack)->inner_scope,
+                                 new_scope, scope_links);
+       }
+       /* This patch is now the current scope */
+       SLIST_INSERT_HEAD(&scope_stack, new_scope, scope_stack_links);
+       return new_scope;
+}
+
+void
+process_scope(scope_t *scope)
+{
+       /*
+        * We are "leaving" this scope.  We should now have
+        * enough information to process the lists of scopes
+        * we encapsulate.
+        */
+       scope_t *cur_scope;
+       u_int skip_patch_count;
+       u_int skip_instr_count;
+
+       cur_scope = TAILQ_LAST(&scope->inner_scope, scope_tailq);
+       skip_patch_count = 0;
+       skip_instr_count = 0;
+       while (cur_scope != NULL) {
+               u_int patch0_patch_skip;
+
+               patch0_patch_skip = 0;
+               switch (cur_scope->type) {
+               case SCOPE_IF:
+               case SCOPE_ELSE_IF:
+                       if (skip_instr_count != 0) {
+                               /* Create a tail patch */
+                               patch0_patch_skip++;
+                               cur_scope->patches[1].skip_patch =
+                                   skip_patch_count + 1;
+                               cur_scope->patches[1].skip_instr =
+                                   skip_instr_count;
+                       }
+
+                       /* Count Head patch */
+                       patch0_patch_skip++;
+
+                       /* Count any patches contained in our inner scope */
+                       patch0_patch_skip += cur_scope->inner_scope_patches;
+
+                       cur_scope->patches[0].skip_patch = patch0_patch_skip;
+                       cur_scope->patches[0].skip_instr =
+                           cur_scope->end_addr - cur_scope->begin_addr;
+
+                       skip_instr_count += cur_scope->patches[0].skip_instr;
+
+                       skip_patch_count += patch0_patch_skip;
+                       if (cur_scope->type == SCOPE_IF) {
+                               scope->inner_scope_patches += skip_patch_count;
+                               skip_patch_count = 0;
+                               skip_instr_count = 0;
+                       }
+                       break;
+               case SCOPE_ELSE:
+                       /* Count any patches contained in our innter scope */
+                       skip_patch_count += cur_scope->inner_scope_patches;
+
+                       skip_instr_count += cur_scope->end_addr
+                                         - cur_scope->begin_addr;
+                       break;
+               case SCOPE_ROOT:
+                       stop("Unexpected scope type encountered", EX_SOFTWARE);
+                       /* NOTREACHED */
+               }
+
+               cur_scope = TAILQ_PREV(cur_scope, scope_tailq, scope_links);
+       }
+}
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm.h b/xen/drivers/scsi/aic7xxx/aicasm/aicasm.h
new file mode 100644 (file)
index 0000000..d18314a
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Assembler for the sequencer program downloaded to Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm.h#10 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm.h,v 1.11.2.4 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+typedef struct path_entry {
+       char    *directory;
+       int     quoted_includes_only;
+       SLIST_ENTRY(path_entry) links;
+} *path_entry_t;
+
+typedef enum {  
+       QUOTED_INCLUDE,
+       BRACKETED_INCLUDE,
+       SOURCE_FILE
+} include_type;
+
+SLIST_HEAD(path_list, path_entry);
+
+extern struct path_list search_path;
+extern struct cs_tailq cs_tailq;
+extern struct scope_list scope_stack;
+extern struct symlist patch_functions;
+extern int includes_search_curdir;             /* False if we've seen -I- */
+extern char *appname;
+extern int yylineno;
+extern char *yyfilename;
+extern char *patch_arg_list;
+extern char *versions;
+extern int   src_mode;
+extern int   dst_mode;
+struct symbol;
+
+void stop(const char *errstring, int err_code);
+void include_file(char *file_name, include_type type);
+void expand_macro(struct symbol *macro_symbol);
+struct instruction *seq_alloc(void);
+struct critical_section *cs_alloc(void);
+struct scope *scope_alloc(void);
+void process_scope(struct scope *);
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
new file mode 100644 (file)
index 0000000..6d9213e
--- /dev/null
@@ -0,0 +1,1858 @@
+%{
+/*
+ * Parser for the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
+ * Copyright (c) 2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_gram.y#15 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_gram.y,v 1.11.2.5 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+#include <sys/types.h>
+
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+int yylineno;
+char *yyfilename;
+char *patch_arg_list;
+char *versions;
+static char errbuf[255];
+static char regex_pattern[255];
+static symbol_t *cur_symbol;
+static symbol_t *scb_or_sram_symbol;
+static symtype cur_symtype;
+static symbol_ref_t accumulator;
+static symbol_ref_t mode_ptr;
+static symbol_ref_t allones;
+static symbol_ref_t allzeros;
+static symbol_ref_t none;
+static symbol_ref_t sindex;
+static int instruction_ptr;
+static int num_srams;
+static int sram_or_scb_offset;
+static int download_constant_count;
+static int in_critical_section;
+
+static void process_bitmask(int mask_type, symbol_t *sym, int mask);
+static void initialize_symbol(symbol_t *symbol);
+static void add_macro_arg(const char *argtext, int position);
+static void add_macro_body(const char *bodytext);
+static void process_register(symbol_t **p_symbol);
+static void format_1_instr(int opcode, symbol_ref_t *dest,
+                          expression_t *immed, symbol_ref_t *src, int ret);
+static void format_2_instr(int opcode, symbol_ref_t *dest,
+                          expression_t *places, symbol_ref_t *src, int ret);
+static void format_3_instr(int opcode, symbol_ref_t *src,
+                          expression_t *immed, symbol_ref_t *address);
+static void test_readable_symbol(symbol_t *symbol);
+static void test_writable_symbol(symbol_t *symbol);
+static void type_check(symbol_t *symbol, expression_t *expression, int and_op);
+static void make_expression(expression_t *immed, int value);
+static void add_conditional(symbol_t *symbol);
+static void add_version(const char *verstring);
+static int  is_download_const(expression_t *immed);
+
+#define SRAM_SYMNAME "SRAM_BASE"
+#define SCB_SYMNAME "SCB_BASE"
+%}
+
+%union {
+       u_int           value;
+       char            *str;
+       symbol_t        *sym;
+       symbol_ref_t    sym_ref;
+       expression_t    expression;
+}
+
+%token T_REGISTER
+
+%token <value> T_CONST
+
+%token T_EXPORT
+
+%token T_DOWNLOAD
+
+%token T_SCB
+
+%token T_SRAM
+
+%token T_ALIAS
+
+%token T_SIZE
+
+%token T_EXPR_LSHIFT
+
+%token T_EXPR_RSHIFT
+
+%token <value> T_ADDRESS
+
+%token T_ACCESS_MODE
+
+%token T_MODES
+
+%token T_DEFINE
+
+%token T_SET_SRC_MODE
+
+%token T_SET_DST_MODE
+
+%token <value> T_MODE
+
+%token T_BEGIN_CS
+
+%token T_END_CS
+
+%token T_BIT
+
+%token T_MASK
+
+%token <value> T_NUMBER
+
+%token <str> T_PATH T_STRING T_ARG T_MACROBODY
+
+%token <sym> T_CEXPR
+
+%token T_EOF T_INCLUDE T_VERSION T_PATCH_ARG_LIST
+
+%token <value> T_SHR T_SHL T_ROR T_ROL
+
+%token <value> T_MVI T_MOV T_CLR T_BMOV
+
+%token <value> T_JMP T_JC T_JNC T_JE T_JNE T_JNZ T_JZ T_CALL
+
+%token <value> T_ADD T_ADC
+
+%token <value> T_INC T_DEC
+
+%token <value> T_STC T_CLC
+
+%token <value> T_CMP T_NOT T_XOR
+
+%token <value> T_TEST T_AND
+
+%token <value> T_OR
+
+%token T_RET
+
+%token T_NOP
+
+%token T_ACCUM T_ALLONES T_ALLZEROS T_NONE T_SINDEX T_MODE_PTR
+
+%token T_A
+
+%token <sym> T_SYMBOL
+
+%token T_NL
+
+%token T_IF T_ELSE T_ELSE_IF T_ENDIF
+
+%type <sym_ref> reg_symbol address destination source opt_source
+
+%type <expression> expression immediate immediate_or_a
+
+%type <value> export ret f1_opcode f2_opcode jmp_jc_jnc_call jz_jnz je_jne
+
+%type <value> numerical_value mode_value mode_list macro_arglist
+
+%left '|'
+%left '&'
+%left T_EXPR_LSHIFT T_EXPR_RSHIFT
+%left '+' '-'
+%left '*' '/'
+%right '~'
+%nonassoc UMINUS
+%%
+
+program:
+       include
+|      program include
+|      patch_arg_list
+|      program patch_arg_list
+|      version
+|      program version
+|      register
+|      program register
+|      constant
+|      program constant
+|      macrodefn
+|      program macrodefn
+|      scratch_ram
+|      program scratch_ram
+|      scb
+|      program scb
+|      label
+|      program label
+|      set_src_mode
+|      program set_src_mode
+|      set_dst_mode
+|      program set_dst_mode
+|      critical_section_start
+|      program critical_section_start
+|      critical_section_end
+|      program critical_section_end
+|      conditional
+|      program conditional
+|      code
+|      program code
+;
+
+include:
+       T_INCLUDE '<' T_PATH '>'
+       {
+               include_file($3, BRACKETED_INCLUDE);
+       }
+|      T_INCLUDE '"' T_PATH '"'
+       {
+               include_file($3, QUOTED_INCLUDE);
+       }
+;
+
+patch_arg_list:
+       T_PATCH_ARG_LIST '=' T_STRING
+       {
+               if (patch_arg_list != NULL)
+                       stop("Patch argument list multiply defined",
+                            EX_DATAERR);
+               patch_arg_list = strdup($3);
+               if (patch_arg_list == NULL)
+                       stop("Unable to record patch arg list", EX_SOFTWARE);
+       }
+;
+
+version:
+       T_VERSION '=' T_STRING
+       { add_version($3); }
+;
+
+register:
+       T_REGISTER { cur_symtype = REGISTER; } reg_definition
+;
+
+reg_definition:
+       T_SYMBOL '{'
+               {
+                       if ($1->type != UNINITIALIZED) {
+                               stop("Register multiply defined", EX_DATAERR);
+                               /* NOTREACHED */
+                       }
+                       cur_symbol = $1; 
+                       cur_symbol->type = cur_symtype;
+                       initialize_symbol(cur_symbol);
+               }
+               reg_attribute_list
+       '}'
+               {                    
+                       /*
+                        * Default to allowing everything in for registers
+                        * with no bit or mask definitions.
+                        */
+                       if (cur_symbol->info.rinfo->valid_bitmask == 0)
+                               cur_symbol->info.rinfo->valid_bitmask = 0xFF;
+
+                       if (cur_symbol->info.rinfo->size == 0)
+                               cur_symbol->info.rinfo->size = 1;
+
+                       /*
+                        * This might be useful for registers too.
+                        */
+                       if (cur_symbol->type != REGISTER) {
+                               if (cur_symbol->info.rinfo->address == 0)
+                                       cur_symbol->info.rinfo->address =
+                                           sram_or_scb_offset;
+                               sram_or_scb_offset +=
+                                   cur_symbol->info.rinfo->size;
+                       }
+                       cur_symbol = NULL;
+               }
+;
+
+reg_attribute_list:
+       reg_attribute
+|      reg_attribute_list reg_attribute
+;
+
+reg_attribute:         
+       reg_address
+|      size
+|      access_mode
+|      modes
+|      bit_defn
+|      mask_defn
+|      alias
+|      accumulator
+|      mode_pointer
+|      allones
+|      allzeros
+|      none
+|      sindex
+;
+
+reg_address:
+       T_ADDRESS T_NUMBER
+       {
+               cur_symbol->info.rinfo->address = $2;
+       }
+;
+
+size:
+       T_SIZE T_NUMBER
+       {
+               cur_symbol->info.rinfo->size = $2;
+               if (scb_or_sram_symbol != NULL) {
+                       u_int max_addr;
+                       u_int sym_max_addr;
+
+                       max_addr = scb_or_sram_symbol->info.rinfo->address
+                                + scb_or_sram_symbol->info.rinfo->size;
+                       sym_max_addr = cur_symbol->info.rinfo->address
+                                    + cur_symbol->info.rinfo->size;
+
+                       if (sym_max_addr > max_addr)
+                               stop("SCB or SRAM space exhausted", EX_DATAERR);
+               }
+       }
+;
+
+access_mode:
+       T_ACCESS_MODE T_MODE
+       {
+               cur_symbol->info.rinfo->mode = $2;
+       }
+;
+
+modes:
+       T_MODES mode_list
+       {
+               cur_symbol->info.rinfo->modes = $2;
+       }
+;
+
+mode_list:
+       mode_value
+       {
+               $$ = $1;
+       }
+|      mode_list ',' mode_value
+       {
+               $$ = $1 | $3;
+       }
+;
+
+mode_value:
+       T_NUMBER
+       {
+               if ($1 > 4) {
+                       stop("Valid register modes range between 0 and 4.",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+
+               $$ = (0x1 << $1);
+       }
+|      T_SYMBOL
+       {
+               symbol_t *symbol;
+
+               symbol = $1;
+               if (symbol->type != CONST) {
+                       stop("Only \"const\" symbols allowed in "
+                            "mode definitions.", EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               if (symbol->info.cinfo->value > 4) {
+                       stop("Valid register modes range between 0 and 4.",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               $$ = (0x1 << symbol->info.cinfo->value);
+       }
+;
+
+bit_defn:
+       T_BIT T_SYMBOL T_NUMBER
+       {
+               process_bitmask(BIT, $2, $3);
+       }
+;
+
+mask_defn:
+       T_MASK T_SYMBOL expression
+       {
+               process_bitmask(MASK, $2, $3.value);
+       }
+;
+
+alias:
+       T_ALIAS T_SYMBOL
+       {
+               if ($2->type != UNINITIALIZED) {
+                       stop("Re-definition of register alias",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               $2->type = ALIAS;
+               initialize_symbol($2);
+               $2->info.ainfo->parent = cur_symbol;
+       }
+;
+
+accumulator:
+       T_ACCUM
+       {
+               if (accumulator.symbol != NULL) {
+                       stop("Only one accumulator definition allowed",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               accumulator.symbol = cur_symbol;
+       }
+;
+
+mode_pointer:
+       T_MODE_PTR
+       {
+               if (mode_ptr.symbol != NULL) {
+                       stop("Only one mode pointer definition allowed",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               mode_ptr.symbol = cur_symbol;
+       }
+;
+
+allones:
+       T_ALLONES
+       {
+               if (allones.symbol != NULL) {
+                       stop("Only one definition of allones allowed",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               allones.symbol = cur_symbol;
+       }
+;
+
+allzeros:
+       T_ALLZEROS
+       {
+               if (allzeros.symbol != NULL) {
+                       stop("Only one definition of allzeros allowed",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               allzeros.symbol = cur_symbol;
+       }
+;
+
+none:
+       T_NONE
+       {
+               if (none.symbol != NULL) {
+                       stop("Only one definition of none allowed",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               none.symbol = cur_symbol;
+       }
+;
+
+sindex:
+       T_SINDEX
+       {
+               if (sindex.symbol != NULL) {
+                       stop("Only one definition of sindex allowed",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               sindex.symbol = cur_symbol;
+       }
+;
+
+expression:
+       expression '|' expression
+       {
+                $$.value = $1.value | $3.value;
+                symlist_merge(&$$.referenced_syms,
+                              &$1.referenced_syms,
+                              &$3.referenced_syms);
+       }
+|      expression '&' expression
+       {
+               $$.value = $1.value & $3.value;
+               symlist_merge(&$$.referenced_syms,
+                              &$1.referenced_syms,
+                              &$3.referenced_syms);
+       }
+|      expression '+' expression
+       {
+               $$.value = $1.value + $3.value;
+               symlist_merge(&$$.referenced_syms,
+                              &$1.referenced_syms,
+                              &$3.referenced_syms);
+       }
+|      expression '-' expression
+       {
+               $$.value = $1.value - $3.value;
+               symlist_merge(&($$.referenced_syms),
+                              &($1.referenced_syms),
+                              &($3.referenced_syms));
+       }
+|      expression '*' expression
+       {
+               $$.value = $1.value * $3.value;
+               symlist_merge(&($$.referenced_syms),
+                              &($1.referenced_syms),
+                              &($3.referenced_syms));
+       }
+|      expression '/' expression
+       {
+               $$.value = $1.value / $3.value;
+               symlist_merge(&($$.referenced_syms),
+                              &($1.referenced_syms),
+                              &($3.referenced_syms));
+       }
+|      expression T_EXPR_LSHIFT expression
+       {
+               $$.value = $1.value << $3.value;
+               symlist_merge(&$$.referenced_syms,
+                              &$1.referenced_syms,
+                              &$3.referenced_syms);
+       }
+|      expression T_EXPR_RSHIFT expression
+       {
+               $$.value = $1.value >> $3.value;
+               symlist_merge(&$$.referenced_syms,
+                              &$1.referenced_syms,
+                              &$3.referenced_syms);
+       }
+|      '(' expression ')'
+       {
+               $$ = $2;
+       }
+|      '~' expression
+       {
+               $$ = $2;
+               $$.value = (~$$.value) & 0xFF;
+       }
+|      '-' expression %prec UMINUS
+       {
+               $$ = $2;
+               $$.value = -$$.value;
+       }
+|      T_NUMBER
+       {
+               $$.value = $1;
+               SLIST_INIT(&$$.referenced_syms);
+       }
+|      T_SYMBOL
+       {
+               symbol_t *symbol;
+
+               symbol = $1;
+               switch (symbol->type) {
+               case ALIAS:
+                       symbol = $1->info.ainfo->parent;
+               case REGISTER:
+               case SCBLOC:
+               case SRAMLOC:
+                       $$.value = symbol->info.rinfo->address;
+                       break;
+               case MASK:
+               case BIT:
+                       $$.value = symbol->info.minfo->mask;
+                       break;
+               case DOWNLOAD_CONST:
+               case CONST:
+                       $$.value = symbol->info.cinfo->value;
+                       break;
+               case UNINITIALIZED:
+               default:
+               {
+                       snprintf(errbuf, sizeof(errbuf),
+                                "Undefined symbol %s referenced",
+                                symbol->name);
+                       stop(errbuf, EX_DATAERR);
+                       /* NOTREACHED */
+                       break;
+               }
+               }
+               SLIST_INIT(&$$.referenced_syms);
+               symlist_add(&$$.referenced_syms, symbol, SYMLIST_INSERT_HEAD);
+       }
+;
+
+constant:
+       T_CONST T_SYMBOL numerical_value
+       {
+               if ($2->type != UNINITIALIZED) {
+                       stop("Re-definition of symbol as a constant",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               $2->type = CONST;
+               initialize_symbol($2);
+               $2->info.cinfo->value = $3;
+       }
+|      T_CONST T_SYMBOL T_DOWNLOAD
+       {
+               if ($1) {
+                       stop("Invalid downloaded constant declaration",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               if ($2->type != UNINITIALIZED) {
+                       stop("Re-definition of symbol as a downloaded constant",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               $2->type = DOWNLOAD_CONST;
+               initialize_symbol($2);
+               $2->info.cinfo->value = download_constant_count++;
+       }
+;
+
+macrodefn_prologue:
+       T_DEFINE T_SYMBOL
+       {
+               if ($2->type != UNINITIALIZED) {
+                       stop("Re-definition of symbol as a macro",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               cur_symbol = $2;
+               cur_symbol->type = MACRO;
+               initialize_symbol(cur_symbol);
+       }
+;
+
+macrodefn:
+       macrodefn_prologue T_MACROBODY
+       {
+               add_macro_body($2);
+       }
+|      macrodefn_prologue '(' macro_arglist ')' T_MACROBODY
+       {
+               add_macro_body($5);
+               cur_symbol->info.macroinfo->narg = $3;
+       }
+;
+
+macro_arglist:
+       {
+               /* Macros can take no arguments */
+               $$ = 0;
+       }
+|      T_ARG
+       {
+               $$ = 1;
+               add_macro_arg($1, 0);
+       }
+|      macro_arglist ',' T_ARG
+       {
+               if ($1 == 0) {
+                       stop("Comma without preceeding argument in arg list",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               $$ = $1 + 1;
+               add_macro_arg($3, $1);
+       }
+;
+
+numerical_value:
+       T_NUMBER
+       {
+               $$ = $1;
+       }
+|      '-' T_NUMBER
+       {
+               $$ = -$2;
+       }
+;
+
+scratch_ram:
+       T_SRAM '{'
+               {
+                       snprintf(errbuf, sizeof(errbuf), "%s%d", SRAM_SYMNAME,
+                                num_srams);
+                       cur_symbol = symtable_get(SRAM_SYMNAME);
+                       cur_symtype = SRAMLOC;
+                       cur_symbol->type = SRAMLOC;
+                       initialize_symbol(cur_symbol);
+               }
+               reg_address
+               {
+                       sram_or_scb_offset = cur_symbol->info.rinfo->address;
+               }
+               size
+               {
+                       scb_or_sram_symbol = cur_symbol;
+               }
+               scb_or_sram_attributes
+       '}'
+               {
+                       cur_symbol = NULL;
+                       scb_or_sram_symbol = NULL;
+               }
+;
+
+scb:
+       T_SCB '{'
+               {
+                       cur_symbol = symtable_get(SCB_SYMNAME);
+                       cur_symtype = SCBLOC;
+                       if (cur_symbol->type != UNINITIALIZED) {
+                               stop("Only one SRAM definition allowed",
+                                    EX_SOFTWARE);
+                               /* NOTREACHED */
+                       }
+                       cur_symbol->type = SCBLOC;
+                       initialize_symbol(cur_symbol);
+                       /* 64 bytes of SCB space */
+                       cur_symbol->info.rinfo->size = 64;
+               }
+               reg_address
+               {
+                       sram_or_scb_offset = cur_symbol->info.rinfo->address;
+               }
+               size
+               {
+                       scb_or_sram_symbol = cur_symbol;
+               }
+               scb_or_sram_attributes
+       '}'
+               {
+                       cur_symbol = NULL;
+                       scb_or_sram_symbol = NULL;
+               }
+;
+
+scb_or_sram_attributes:
+       /* NULL definition is okay */
+|      modes
+|      scb_or_sram_reg_list
+|      modes scb_or_sram_reg_list
+;
+
+scb_or_sram_reg_list:
+       reg_definition
+|      scb_or_sram_reg_list reg_definition
+;
+
+reg_symbol:
+       T_SYMBOL
+       {
+               process_register(&$1);
+               $$.symbol = $1;
+               $$.offset = 0;
+       }
+|      T_SYMBOL '[' T_SYMBOL ']'
+       {
+               process_register(&$1);
+               if ($3->type != CONST) {
+                       stop("register offset must be a constant", EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               if (($3->info.cinfo->value + 1) > $1->info.rinfo->size) {
+                       stop("Accessing offset beyond range of register",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               $$.symbol = $1;
+               $$.offset = $3->info.cinfo->value;
+       }
+|      T_SYMBOL '[' T_NUMBER ']'
+       {
+               process_register(&$1);
+               if (($3 + 1) > $1->info.rinfo->size) {
+                       stop("Accessing offset beyond range of register",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               $$.symbol = $1;
+               $$.offset = $3;
+       }
+|      T_A
+       {
+               if (accumulator.symbol == NULL) {
+                       stop("No accumulator has been defined", EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               $$.symbol = accumulator.symbol;
+               $$.offset = 0;
+       }
+;
+
+destination:
+       reg_symbol
+       {
+               test_writable_symbol($1.symbol);
+               $$ = $1;
+       }
+;
+
+immediate:
+       expression
+       { $$ = $1; }
+;
+
+immediate_or_a:
+       expression
+       {
+               if ($1.value == 0 && is_download_const(&$1) == 0) {
+                       snprintf(errbuf, sizeof(errbuf),
+                                "\nExpression evaluates to 0 and thus "
+                                "references the accumulator.\n "
+                                "If this is the desired effect, use 'A' "
+                                "instead.\n");
+                       stop(errbuf, EX_DATAERR);
+               }
+               $$ = $1;
+       }
+|      T_A
+       {
+               SLIST_INIT(&$$.referenced_syms);
+               $$.value = 0;
+       }
+;
+
+source:
+       reg_symbol
+       {
+               test_readable_symbol($1.symbol);
+               $$ = $1;
+       }
+;
+
+opt_source:
+       {
+               $$.symbol = NULL;
+               $$.offset = 0;
+       }
+|      ',' source
+       { $$ = $2; }
+;
+
+ret:
+       { $$ = 0; }
+|      T_RET
+       { $$ = 1; }
+;
+
+set_src_mode:
+       T_SET_SRC_MODE T_NUMBER ';'
+       {
+               src_mode = $2;
+       }
+;
+
+set_dst_mode:
+       T_SET_DST_MODE T_NUMBER ';'
+       {
+               dst_mode = $2;
+       }
+;
+
+critical_section_start:
+       T_BEGIN_CS ';'
+       {
+               critical_section_t *cs;
+
+               if (in_critical_section != FALSE) {
+                       stop("Critical Section within Critical Section",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               cs = cs_alloc();
+               cs->begin_addr = instruction_ptr;
+               in_critical_section = TRUE;
+       }
+
+critical_section_end:
+       T_END_CS ';'
+       {
+               critical_section_t *cs;
+
+               if (in_critical_section == FALSE) {
+                       stop("Unballanced 'end_cs'", EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               cs = TAILQ_LAST(&cs_tailq, cs_tailq);
+               cs->end_addr = instruction_ptr;
+               in_critical_section = FALSE;
+       }
+
+export:
+       { $$ = 0; }
+|      T_EXPORT
+       { $$ = 1; }
+;
+
+label:
+       export T_SYMBOL ':'
+       {
+               if ($2->type != UNINITIALIZED) {
+                       stop("Program label multiply defined", EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               $2->type = LABEL;
+               initialize_symbol($2);
+               $2->info.linfo->address = instruction_ptr;
+               $2->info.linfo->exported = $1;
+       }
+;
+
+address:
+       T_SYMBOL
+       {
+               $$.symbol = $1;
+               $$.offset = 0;
+       }
+|      T_SYMBOL '+' T_NUMBER
+       {
+               $$.symbol = $1;
+               $$.offset = $3;
+       }
+|      T_SYMBOL '-' T_NUMBER
+       {
+               $$.symbol = $1;
+               $$.offset = -$3;
+       }
+|      '.'
+       {
+               $$.symbol = NULL;
+               $$.offset = 0;
+       }
+|      '.' '+' T_NUMBER
+       {
+               $$.symbol = NULL;
+               $$.offset = $3;
+       }
+|      '.' '-' T_NUMBER
+       {
+               $$.symbol = NULL;
+               $$.offset = -$3;
+       }
+;
+
+conditional:
+       T_IF T_CEXPR '{'
+       {
+               scope_t *new_scope;
+
+               add_conditional($2);
+               new_scope = scope_alloc();
+               new_scope->type = SCOPE_IF;
+               new_scope->begin_addr = instruction_ptr;
+               new_scope->func_num = $2->info.condinfo->func_num;
+       }
+|      T_ELSE T_IF T_CEXPR '{'
+       {
+               scope_t *new_scope;
+               scope_t *scope_context;
+               scope_t *last_scope;
+
+               /*
+                * Ensure that the previous scope is either an
+                * if or and else if.
+                */
+               scope_context = SLIST_FIRST(&scope_stack);
+               last_scope = TAILQ_LAST(&scope_context->inner_scope,
+                                       scope_tailq);
+               if (last_scope == NULL
+                || last_scope->type == T_ELSE) {
+
+                       stop("'else if' without leading 'if'", EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               add_conditional($3);
+               new_scope = scope_alloc();
+               new_scope->type = SCOPE_ELSE_IF;
+               new_scope->begin_addr = instruction_ptr;
+               new_scope->func_num = $3->info.condinfo->func_num;
+       }
+|      T_ELSE '{'
+       {
+               scope_t *new_scope;
+               scope_t *scope_context;
+               scope_t *last_scope;
+
+               /*
+                * Ensure that the previous scope is either an
+                * if or and else if.
+                */
+               scope_context = SLIST_FIRST(&scope_stack);
+               last_scope = TAILQ_LAST(&scope_context->inner_scope,
+                                       scope_tailq);
+               if (last_scope == NULL
+                || last_scope->type == SCOPE_ELSE) {
+
+                       stop("'else' without leading 'if'", EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               new_scope = scope_alloc();
+               new_scope->type = SCOPE_ELSE;
+               new_scope->begin_addr = instruction_ptr;
+       }
+;
+
+conditional:
+       '}'
+       {
+               scope_t *scope_context;
+
+               scope_context = SLIST_FIRST(&scope_stack);
+               if (scope_context->type == SCOPE_ROOT) {
+                       stop("Unexpected '}' encountered", EX_DATAERR);
+                       /* NOTREACHED */
+               }
+
+               scope_context->end_addr = instruction_ptr;
+
+               /* Pop the scope */
+               SLIST_REMOVE_HEAD(&scope_stack, scope_stack_links);
+
+               process_scope(scope_context);
+
+               if (SLIST_FIRST(&scope_stack) == NULL) {
+                       stop("Unexpected '}' encountered", EX_DATAERR);
+                       /* NOTREACHED */
+               }
+       }
+;
+
+f1_opcode:
+       T_AND { $$ = AIC_OP_AND; }
+|      T_XOR { $$ = AIC_OP_XOR; }
+|      T_ADD { $$ = AIC_OP_ADD; }
+|      T_ADC { $$ = AIC_OP_ADC; }
+;
+
+code:
+       f1_opcode destination ',' immediate_or_a opt_source ret ';'
+       {
+               format_1_instr($1, &$2, &$4, &$5, $6);
+       }
+;
+
+code:
+       T_OR reg_symbol ',' immediate_or_a opt_source ret ';'
+       {
+               format_1_instr(AIC_OP_OR, &$2, &$4, &$5, $6);
+       }
+;
+
+code:
+       T_INC destination opt_source ret ';'
+       {
+               expression_t immed;
+
+               make_expression(&immed, 1);
+               format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4);
+       }
+;
+
+code:
+       T_DEC destination opt_source ret ';'
+       {
+               expression_t immed;
+
+               make_expression(&immed, -1);
+               format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4);
+       }
+;
+
+code:
+       T_CLC ret ';'
+       {
+               expression_t immed;
+
+               make_expression(&immed, -1);
+               format_1_instr(AIC_OP_ADD, &none, &immed, &allzeros, $2);
+       }
+|      T_CLC T_MVI destination ',' immediate_or_a ret ';'
+       {
+               format_1_instr(AIC_OP_ADD, &$3, &$5, &allzeros, $6);
+       }
+;
+
+code:
+       T_STC ret ';'
+       {
+               expression_t immed;
+
+               make_expression(&immed, 1);
+               format_1_instr(AIC_OP_ADD, &none, &immed, &allones, $2);
+       }
+|      T_STC destination ret ';'
+       {
+               expression_t immed;
+
+               make_expression(&immed, 1);
+               format_1_instr(AIC_OP_ADD, &$2, &immed, &allones, $3);
+       }
+;
+
+code:
+       T_BMOV destination ',' source ',' immediate ret ';'
+       {
+               format_1_instr(AIC_OP_BMOV, &$2, &$6, &$4, $7);
+       }
+;
+
+code:
+       T_MOV destination ',' source ret ';'
+       {
+               expression_t immed;
+
+               make_expression(&immed, 1);
+               format_1_instr(AIC_OP_BMOV, &$2, &immed, &$4, $5);
+       }
+;
+
+code:
+       T_MVI destination ',' immediate_or_a ret ';'
+       {
+               format_1_instr(AIC_OP_OR, &$2, &$4, &allzeros, $5);
+       }
+;
+
+code:
+       T_NOT destination opt_source ret ';'
+       {
+               expression_t immed;
+
+               make_expression(&immed, 0xff);
+               format_1_instr(AIC_OP_XOR, &$2, &immed, &$3, $4);
+       }
+;
+
+code:
+       T_CLR destination ret ';'
+       {
+               expression_t immed;
+
+               make_expression(&immed, 0xff);
+               format_1_instr(AIC_OP_AND, &$2, &immed, &allzeros, $3);
+       }
+;
+
+code:
+       T_NOP ret ';'
+       {
+               expression_t immed;
+
+               make_expression(&immed, 0xff);
+               format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, $2);
+       }
+;
+
+code:
+       T_RET ';'
+       {
+               expression_t immed;
+
+               make_expression(&immed, 0xff);
+               format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, TRUE);
+       }
+;
+
+       /*
+        * This grammer differs from the one in the aic7xxx
+        * reference manual since the grammer listed there is
+        * ambiguous and causes a shift/reduce conflict.
+        * It also seems more logical as the "immediate"
+        * argument is listed as the second arg like the
+        * other formats.
+        */
+
+f2_opcode:
+       T_SHL { $$ = AIC_OP_SHL; }
+|      T_SHR { $$ = AIC_OP_SHR; }
+|      T_ROL { $$ = AIC_OP_ROL; }
+|      T_ROR { $$ = AIC_OP_ROR; }
+;
+
+code:
+       f2_opcode destination ',' expression opt_source ret ';'
+       {
+               format_2_instr($1, &$2, &$4, &$5, $6);
+       }
+;
+
+jmp_jc_jnc_call:
+       T_JMP   { $$ = AIC_OP_JMP; }
+|      T_JC    { $$ = AIC_OP_JC; }
+|      T_JNC   { $$ = AIC_OP_JNC; }
+|      T_CALL  { $$ = AIC_OP_CALL; }
+;
+
+jz_jnz:
+       T_JZ    { $$ = AIC_OP_JZ; }
+|      T_JNZ   { $$ = AIC_OP_JNZ; }
+;
+
+je_jne:
+       T_JE    { $$ = AIC_OP_JE; }
+|      T_JNE   { $$ = AIC_OP_JNE; }
+;
+
+code:
+       jmp_jc_jnc_call address ';'
+       {
+               expression_t immed;
+
+               make_expression(&immed, 0);
+               format_3_instr($1, &sindex, &immed, &$2);
+       }
+;
+
+code:
+       T_OR reg_symbol ',' immediate jmp_jc_jnc_call address ';'
+       {
+               format_3_instr($5, &$2, &$4, &$6);
+       }
+;
+
+code:
+       T_TEST source ',' immediate_or_a jz_jnz address ';'
+       {
+               format_3_instr($5, &$2, &$4, &$6);
+       }
+;
+
+code:
+       T_CMP source ',' immediate_or_a je_jne address ';'
+       {
+               format_3_instr($5, &$2, &$4, &$6);
+       }
+;
+
+code:
+       T_MOV source jmp_jc_jnc_call address ';'
+       {
+               expression_t immed;
+
+               make_expression(&immed, 0);
+               format_3_instr($3, &$2, &immed, &$4);
+       }
+;
+
+code:
+       T_MVI immediate jmp_jc_jnc_call address ';'
+       {
+               format_3_instr($3, &allzeros, &$2, &$4);
+       }
+;
+
+%%
+
+static void
+process_bitmask(int mask_type, symbol_t *sym, int mask)
+{
+       /*
+        * Add the current register to its
+        * symbol list, if it already exists,
+        * warn if we are setting it to a
+        * different value, or in the bit to
+        * the "allowed bits" of this register.
+        */
+       if (sym->type == UNINITIALIZED) {
+               sym->type = mask_type;
+               initialize_symbol(sym);
+               if (mask_type == BIT) {
+                       if (mask == 0) {
+                               stop("Bitmask with no bits set", EX_DATAERR);
+                               /* NOTREACHED */
+                       }
+                       if ((mask & ~(0x01 << (ffs(mask) - 1))) != 0) {
+                               stop("Bitmask with more than one bit set",
+                                    EX_DATAERR);
+                               /* NOTREACHED */
+                       }
+               }
+               sym->info.minfo->mask = mask;
+       } else if (sym->type != mask_type) {
+               stop("Bit definition mirrors a definition of the same "
+                    " name, but a different type", EX_DATAERR);
+               /* NOTREACHED */
+       } else if (mask != sym->info.minfo->mask) {
+               stop("Bitmask redefined with a conflicting value", EX_DATAERR);
+               /* NOTREACHED */
+       }
+       /* Fail if this symbol is already listed */
+       if (symlist_search(&(sym->info.minfo->symrefs),
+                          cur_symbol->name) != NULL) {
+               stop("Bitmask defined multiple times for register", EX_DATAERR);
+               /* NOTREACHED */
+       }
+       symlist_add(&(sym->info.minfo->symrefs), cur_symbol,
+                   SYMLIST_INSERT_HEAD);
+       cur_symbol->info.rinfo->valid_bitmask |= mask;
+       cur_symbol->info.rinfo->typecheck_masks = TRUE;
+}
+
+static void
+initialize_symbol(symbol_t *symbol)
+{
+       switch (symbol->type) {
+        case UNINITIALIZED:
+               stop("Call to initialize_symbol with type field unset",
+                    EX_SOFTWARE);
+               /* NOTREACHED */
+               break;
+        case REGISTER:
+        case SRAMLOC:
+        case SCBLOC:
+               symbol->info.rinfo =
+                   (struct reg_info *)malloc(sizeof(struct reg_info));
+               if (symbol->info.rinfo == NULL) {
+                       stop("Can't create register info", EX_SOFTWARE);
+                       /* NOTREACHED */
+               }
+               memset(symbol->info.rinfo, 0,
+                      sizeof(struct reg_info));
+               /*
+                * Default to allowing access in all register modes
+                * or to the mode specified by the SCB or SRAM space
+                * we are in.
+                */
+               if (scb_or_sram_symbol != NULL)
+                       symbol->info.rinfo->modes =
+                           scb_or_sram_symbol->info.rinfo->modes;
+               else
+                       symbol->info.rinfo->modes = ~0;
+               break;
+        case ALIAS:
+               symbol->info.ainfo =
+                   (struct alias_info *)malloc(sizeof(struct alias_info));
+               if (symbol->info.ainfo == NULL) {
+                       stop("Can't create alias info", EX_SOFTWARE);
+                       /* NOTREACHED */
+               }
+               memset(symbol->info.ainfo, 0,
+                      sizeof(struct alias_info));
+               break;
+        case MASK:
+        case BIT:
+               symbol->info.minfo =
+                   (struct mask_info *)malloc(sizeof(struct mask_info));
+               if (symbol->info.minfo == NULL) {
+                       stop("Can't create bitmask info", EX_SOFTWARE);
+                       /* NOTREACHED */
+               }
+               memset(symbol->info.minfo, 0, sizeof(struct mask_info));
+               SLIST_INIT(&(symbol->info.minfo->symrefs));
+               break;
+        case CONST:
+        case DOWNLOAD_CONST:
+               symbol->info.cinfo =
+                   (struct const_info *)malloc(sizeof(struct const_info));
+               if (symbol->info.cinfo == NULL) {
+                       stop("Can't create alias info", EX_SOFTWARE);
+                       /* NOTREACHED */
+               }
+               memset(symbol->info.cinfo, 0,
+                      sizeof(struct const_info));
+               break;
+       case LABEL:
+               symbol->info.linfo =
+                   (struct label_info *)malloc(sizeof(struct label_info));
+               if (symbol->info.linfo == NULL) {
+                       stop("Can't create label info", EX_SOFTWARE);
+                       /* NOTREACHED */
+               }
+               memset(symbol->info.linfo, 0,
+                      sizeof(struct label_info));
+               break;
+       case CONDITIONAL:
+               symbol->info.condinfo =
+                   (struct cond_info *)malloc(sizeof(struct cond_info));
+               if (symbol->info.condinfo == NULL) {
+                       stop("Can't create conditional info", EX_SOFTWARE);
+                       /* NOTREACHED */
+               }
+               memset(symbol->info.condinfo, 0,
+                      sizeof(struct cond_info));
+               break;
+       case MACRO:
+               symbol->info.macroinfo = 
+                   (struct macro_info *)malloc(sizeof(struct macro_info));
+               if (symbol->info.macroinfo == NULL) {
+                       stop("Can't create macro info", EX_SOFTWARE);
+                       /* NOTREACHED */
+               }
+               memset(symbol->info.macroinfo, 0,
+                      sizeof(struct macro_info));
+               STAILQ_INIT(&symbol->info.macroinfo->args);
+               break;
+       default:
+               stop("Call to initialize_symbol with invalid symbol type",
+                    EX_SOFTWARE);
+               /* NOTREACHED */
+               break;
+       }
+}
+
+static void
+add_macro_arg(const char *argtext, int argnum)
+{
+       struct macro_arg *marg;
+       int i;
+       int retval;
+               
+
+       if (cur_symbol == NULL || cur_symbol->type != MACRO) {
+               stop("Invalid current symbol for adding macro arg",
+                    EX_SOFTWARE);
+               /* NOTREACHED */
+       }
+
+       marg = (struct macro_arg *)malloc(sizeof(*marg));
+       if (marg == NULL) {
+               stop("Can't create macro_arg structure", EX_SOFTWARE);
+               /* NOTREACHED */
+       }
+       marg->replacement_text = NULL;
+       retval = snprintf(regex_pattern, sizeof(regex_pattern),
+                         "[^-/A-Za-z0-9_](%s)([^-/A-Za-z0-9_]|$)",
+                         argtext);
+       if (retval >= sizeof(regex_pattern)) {
+               stop("Regex text buffer too small for arg",
+                    EX_SOFTWARE);
+               /* NOTREACHED */
+       }
+       retval = regcomp(&marg->arg_regex, regex_pattern, REG_EXTENDED);
+       if (retval != 0) {
+               stop("Regex compilation failed", EX_SOFTWARE);
+               /* NOTREACHED */
+       }
+       STAILQ_INSERT_TAIL(&cur_symbol->info.macroinfo->args, marg, links);
+}
+
+static void
+add_macro_body(const char *bodytext)
+{
+       if (cur_symbol == NULL || cur_symbol->type != MACRO) {
+               stop("Invalid current symbol for adding macro arg",
+                    EX_SOFTWARE);
+               /* NOTREACHED */
+       }
+       cur_symbol->info.macroinfo->body = strdup(bodytext);
+       if (cur_symbol->info.macroinfo->body == NULL) {
+               stop("Can't duplicate macro body text", EX_SOFTWARE);
+               /* NOTREACHED */
+       }
+}
+
+static void
+process_register(symbol_t **p_symbol)
+{
+       symbol_t *symbol = *p_symbol;
+
+       if (symbol->type == UNINITIALIZED) {
+               snprintf(errbuf, sizeof(errbuf), "Undefined register %s",
+                        symbol->name);
+               stop(errbuf, EX_DATAERR);
+               /* NOTREACHED */
+       } else if (symbol->type == ALIAS) {
+               *p_symbol = symbol->info.ainfo->parent;
+       } else if ((symbol->type != REGISTER)
+               && (symbol->type != SCBLOC)
+               && (symbol->type != SRAMLOC)) {
+               snprintf(errbuf, sizeof(errbuf),
+                        "Specified symbol %s is not a register",
+                        symbol->name);
+               stop(errbuf, EX_DATAERR);
+       }
+}
+
+static void
+format_1_instr(int opcode, symbol_ref_t *dest, expression_t *immed,
+              symbol_ref_t *src, int ret)
+{
+       struct instruction *instr;
+       struct ins_format1 *f1_instr;
+
+       if (src->symbol == NULL)
+               src = dest;
+
+       /* Test register permissions */
+       test_writable_symbol(dest->symbol);
+       test_readable_symbol(src->symbol);
+
+       /* Ensure that immediate makes sense for this destination */
+       type_check(dest->symbol, immed, opcode);
+
+       /* Allocate sequencer space for the instruction and fill it out */
+       instr = seq_alloc();
+       f1_instr = &instr->format.format1;
+       f1_instr->ret = ret ? 1 : 0;
+       f1_instr->opcode = opcode;
+       f1_instr->destination = dest->symbol->info.rinfo->address
+                             + dest->offset;
+       f1_instr->source = src->symbol->info.rinfo->address
+                        + src->offset;
+       f1_instr->immediate = immed->value;
+
+       if (is_download_const(immed))
+               f1_instr->parity = 1;
+       else if (dest->symbol == mode_ptr.symbol) {
+               u_int src_value;
+               u_int dst_value;
+
+               /*
+                * Attempt to update mode information if
+                * we are operating on the mode register.
+                */
+               if (src->symbol == allones.symbol)
+                       src_value = 0xFF;
+               else if (src->symbol == allzeros.symbol)
+                       src_value = 0;
+               else if (src->symbol == mode_ptr.symbol)
+                       src_value = (dst_mode << 4) | src_mode;
+               else
+                       goto cant_update;
+
+               switch (opcode) {
+               case AIC_OP_AND:
+                       dst_value = src_value & immed->value;
+                       break;
+               case AIC_OP_XOR:
+                       dst_value = src_value ^ immed->value;
+                       break;
+               case AIC_OP_ADD:
+                       dst_value = (src_value + immed->value) & 0xFF;
+                       break;
+               case AIC_OP_OR:
+                       dst_value = src_value | immed->value;
+                       break;
+                       break;
+               case AIC_OP_BMOV:
+                       dst_value = src_value;
+                       break;
+               default:
+                       goto cant_update;
+               }
+               src_mode = dst_value & 0xF;
+               dst_mode = (dst_value >> 4) & 0xF;
+cant_update:
+       }
+
+       symlist_free(&immed->referenced_syms);
+       instruction_ptr++;
+}
+
+static void
+format_2_instr(int opcode, symbol_ref_t *dest, expression_t *places,
+              symbol_ref_t *src, int ret)
+{
+       struct instruction *instr;
+       struct ins_format2 *f2_instr;
+       uint8_t shift_control;
+
+       if (src->symbol == NULL)
+               src = dest;
+
+       /* Test register permissions */
+       test_writable_symbol(dest->symbol);
+       test_readable_symbol(src->symbol);
+
+       /* Allocate sequencer space for the instruction and fill it out */
+       instr = seq_alloc();
+       f2_instr = &instr->format.format2;
+       f2_instr->ret = ret ? 1 : 0;
+       f2_instr->opcode = AIC_OP_ROL;
+       f2_instr->destination = dest->symbol->info.rinfo->address
+                             + dest->offset;
+       f2_instr->source = src->symbol->info.rinfo->address
+                        + src->offset;
+       if (places->value > 8 || places->value <= 0) {
+               stop("illegal shift value", EX_DATAERR);
+               /* NOTREACHED */
+       }
+       switch (opcode) {
+       case AIC_OP_SHL:
+               if (places->value == 8)
+                       shift_control = 0xf0;
+               else
+                       shift_control = (places->value << 4) | places->value;
+               break;
+       case AIC_OP_SHR:
+               if (places->value == 8) {
+                       shift_control = 0xf8;
+               } else {
+                       shift_control = (places->value << 4)
+                                     | (8 - places->value)
+                                     | 0x08;
+               }
+               break;
+       case AIC_OP_ROL:
+               shift_control = places->value & 0x7;
+               break;
+       case AIC_OP_ROR:
+               shift_control = (8 - places->value) | 0x08;
+               break;
+       default:
+               shift_control = 0; /* Quiet Compiler */
+               stop("Invalid shift operation specified", EX_SOFTWARE);
+               /* NOTREACHED */
+               break;
+       };
+       f2_instr->shift_control = shift_control;
+       symlist_free(&places->referenced_syms);
+       instruction_ptr++;
+}
+
+static void
+format_3_instr(int opcode, symbol_ref_t *src,
+              expression_t *immed, symbol_ref_t *address)
+{
+       struct instruction *instr;
+       struct ins_format3 *f3_instr;
+       int addr;
+
+       /* Test register permissions */
+       test_readable_symbol(src->symbol);
+
+       /* Ensure that immediate makes sense for this source */
+       type_check(src->symbol, immed, opcode);
+
+       /* Allocate sequencer space for the instruction and fill it out */
+       instr = seq_alloc();
+       f3_instr = &instr->format.format3;
+       if (address->symbol == NULL) {
+               /* 'dot' referrence.  Use the current instruction pointer */
+               addr = instruction_ptr + address->offset;
+       } else if (address->symbol->type == UNINITIALIZED) {
+               /* forward reference */
+               addr = address->offset;
+               instr->patch_label = address->symbol;
+       } else
+               addr = address->symbol->info.linfo->address + address->offset;
+       f3_instr->opcode = opcode;
+       f3_instr->address = addr;
+       f3_instr->source = src->symbol->info.rinfo->address
+                        + src->offset;
+       f3_instr->immediate = immed->value;
+
+       if (is_download_const(immed))
+               f3_instr->parity = 1;
+
+       symlist_free(&immed->referenced_syms);
+       instruction_ptr++;
+}
+
+static void
+test_readable_symbol(symbol_t *symbol)
+{
+       
+       if ((symbol->info.rinfo->modes & (0x1 << src_mode)) == 0) {
+               snprintf(errbuf, sizeof(errbuf),
+                       "Register %s unavailable in source reg mode %d",
+                       symbol->name, src_mode);
+               stop(errbuf, EX_DATAERR);
+       }
+
+       if (symbol->info.rinfo->mode == WO) {
+               stop("Write Only register specified as source",
+                    EX_DATAERR);
+               /* NOTREACHED */
+       }
+}
+
+static void
+test_writable_symbol(symbol_t *symbol)
+{
+       
+       if ((symbol->info.rinfo->modes & (0x1 << dst_mode)) == 0) {
+               snprintf(errbuf, sizeof(errbuf),
+                       "Register %s unavailable in destination reg mode %d",
+                       symbol->name, dst_mode);
+               stop(errbuf, EX_DATAERR);
+       }
+
+       if (symbol->info.rinfo->mode == RO) {
+               stop("Read Only register specified as destination",
+                    EX_DATAERR);
+               /* NOTREACHED */
+       }
+}
+
+static void
+type_check(symbol_t *symbol, expression_t *expression, int opcode)
+{
+       symbol_node_t *node;
+       int and_op;
+
+       and_op = FALSE;
+       if (opcode == AIC_OP_AND || opcode == AIC_OP_JNZ || AIC_OP_JZ)
+               and_op = TRUE;
+
+       /*
+        * Make sure that we aren't attempting to write something
+        * that hasn't been defined.  If this is an and operation,
+        * this is a mask, so "undefined" bits are okay.
+        */
+       if (and_op == FALSE
+        && (expression->value & ~symbol->info.rinfo->valid_bitmask) != 0) {
+               snprintf(errbuf, sizeof(errbuf),
+                        "Invalid bit(s) 0x%x in immediate written to %s",
+                        expression->value & ~symbol->info.rinfo->valid_bitmask,
+                        symbol->name);
+               stop(errbuf, EX_DATAERR);
+               /* NOTREACHED */
+       }
+
+       /*
+        * Now make sure that all of the symbols referenced by the
+        * expression are defined for this register.
+        */
+       if (symbol->info.rinfo->typecheck_masks != FALSE) {
+               for(node = expression->referenced_syms.slh_first;
+                   node != NULL;
+                   node = node->links.sle_next) {
+                       if ((node->symbol->type == MASK
+                         || node->symbol->type == BIT)
+                        && symlist_search(&node->symbol->info.minfo->symrefs,
+                                          symbol->name) == NULL) {
+                               snprintf(errbuf, sizeof(errbuf),
+                                        "Invalid bit or mask %s "
+                                        "for register %s",
+                                        node->symbol->name, symbol->name);
+                               stop(errbuf, EX_DATAERR);
+                               /* NOTREACHED */
+                       }
+               }
+       }
+}
+
+static void
+make_expression(expression_t *immed, int value)
+{
+       SLIST_INIT(&immed->referenced_syms);
+       immed->value = value & 0xff;
+}
+
+static void
+add_conditional(symbol_t *symbol)
+{
+       static int numfuncs;
+
+       if (numfuncs == 0) {
+               /* add a special conditional, "0" */
+               symbol_t *false_func;
+
+               false_func = symtable_get("0");
+               if (false_func->type != UNINITIALIZED) {
+                       stop("Conditional expression '0' "
+                            "conflicts with a symbol", EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               false_func->type = CONDITIONAL;
+               initialize_symbol(false_func);
+               false_func->info.condinfo->func_num = numfuncs++;
+               symlist_add(&patch_functions, false_func, SYMLIST_INSERT_HEAD);
+       }
+
+       /* This condition has occurred before */
+       if (symbol->type == CONDITIONAL)
+               return;
+
+       if (symbol->type != UNINITIALIZED) {
+               stop("Conditional expression conflicts with a symbol",
+                    EX_DATAERR);
+               /* NOTREACHED */
+       }
+
+       symbol->type = CONDITIONAL;
+       initialize_symbol(symbol);
+       symbol->info.condinfo->func_num = numfuncs++;
+       symlist_add(&patch_functions, symbol, SYMLIST_INSERT_HEAD);
+}
+
+static void
+add_version(const char *verstring)
+{
+       const char prefix[] = " * ";
+       int newlen;
+       int oldlen;
+
+       newlen = strlen(verstring) + strlen(prefix);
+       oldlen = 0;
+       if (versions != NULL)
+               oldlen = strlen(versions);
+       versions = realloc(versions, newlen + oldlen + 2);
+       if (versions == NULL)
+               stop("Can't allocate version string", EX_SOFTWARE);
+       strcpy(&versions[oldlen], prefix);
+       strcpy(&versions[oldlen + strlen(prefix)], verstring);
+       versions[newlen + oldlen] = '\n';
+       versions[newlen + oldlen + 1] = '\0';
+}
+
+void
+yyerror(const char *string)
+{
+       stop(string, EX_DATAERR);
+}
+
+static int
+is_download_const(expression_t *immed)
+{
+       if ((immed->referenced_syms.slh_first != NULL)
+        && (immed->referenced_syms.slh_first->symbol->type == DOWNLOAD_CONST))
+               return (TRUE);
+
+       return (FALSE);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
new file mode 100644 (file)
index 0000000..aeba6da
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Instruction formats for the sequencer program downloaded to
+ * Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_insformat.h#9 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_insformat.h,v 1.3.2.3 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+struct ins_format1 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+       uint32_t        immediate       : 8,
+                       source          : 9,
+                       destination     : 9,
+                       ret             : 1,
+                       opcode          : 4,
+                       parity          : 1;
+#else
+       uint32_t        parity          : 1,
+                       opcode          : 4,
+                       ret             : 1,
+                       destination     : 9,
+                       source          : 9,
+                       immediate       : 8;
+#endif
+};
+
+struct ins_format2 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+       uint32_t        shift_control   : 8,
+                       source          : 9,
+                       destination     : 9,
+                       ret             : 1,
+                       opcode          : 4,
+                       parity          : 1;
+#else
+       uint32_t        parity          : 1,
+                       opcode          : 4,
+                       ret             : 1,
+                       destination     : 9,
+                       source          : 9,
+                       shift_control   : 8;
+#endif
+};
+
+struct ins_format3 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+       uint32_t        immediate       : 8,
+                       source          : 9,
+                       address         : 10,
+                       opcode          : 4,
+                       parity          : 1;
+#else
+       uint32_t        parity          : 1,
+                       opcode          : 4,
+                       address         : 10,
+                       source          : 9,
+                       immediate       : 8;
+#endif
+};
+
+union ins_formats {
+               struct ins_format1 format1;
+               struct ins_format2 format2;
+               struct ins_format3 format3;
+               uint8_t            bytes[4];
+               uint32_t           integer;
+};
+struct instruction {
+       union   ins_formats format;
+       u_int   srcline;
+       struct symbol *patch_label;
+       STAILQ_ENTRY(instruction) links;
+};
+
+#define        AIC_OP_OR       0x0
+#define        AIC_OP_AND      0x1
+#define AIC_OP_XOR     0x2
+#define        AIC_OP_ADD      0x3
+#define        AIC_OP_ADC      0x4
+#define        AIC_OP_ROL      0x5
+#define        AIC_OP_BMOV     0x6
+
+#define        AIC_OP_JMP      0x8
+#define AIC_OP_JC      0x9
+#define AIC_OP_JNC     0xa
+#define AIC_OP_CALL    0xb
+#define        AIC_OP_JNE      0xc
+#define        AIC_OP_JNZ      0xd
+#define        AIC_OP_JE       0xe
+#define        AIC_OP_JZ       0xf
+
+/* Pseudo Ops */
+#define        AIC_OP_SHL      0x10
+#define        AIC_OP_SHR      0x20
+#define        AIC_OP_ROR      0x30
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_gram.y
new file mode 100644 (file)
index 0000000..dabc6c2
--- /dev/null
@@ -0,0 +1,164 @@
+%{
+/*
+ * Sub-parser for macro invocation in the Aic7xxx SCSI
+ * Host adapter sequencer assembler.
+ *
+ * Copyright (c) 2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id$
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_macro_gram.y,v 1.1.2.1 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+#include <sys/types.h>
+
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+static symbol_t *macro_symbol;
+
+static void add_macro_arg(const char *argtext, int position);
+
+%}
+
+%union {
+       int             value;
+       char            *str;
+       symbol_t        *sym;
+}
+
+
+%token <str> T_ARG
+
+%token <sym> T_SYMBOL
+
+%type <value> macro_arglist
+
+%%
+
+macrocall:
+       T_SYMBOL '('
+       {
+               macro_symbol = $1;
+       }
+       macro_arglist ')'
+       {
+               if (macro_symbol->info.macroinfo->narg != $4) {
+                       printf("Narg == %d", macro_symbol->info.macroinfo->narg);
+                       stop("Too few arguments for macro invocation",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               macro_symbol = NULL;
+               YYACCEPT;
+       }
+;
+
+macro_arglist:
+       {
+               /* Macros can take 0 arguments */
+               $$ = 0;
+       }
+|      T_ARG
+       {
+               $$ = 1;
+               add_macro_arg($1, 1);
+       }
+|      macro_arglist ',' T_ARG
+       {
+               if ($1 == 0) {
+                       stop("Comma without preceeding argument in arg list",
+                            EX_DATAERR);
+                       /* NOTREACHED */
+               }
+               $$ = $1 + 1;
+               add_macro_arg($3, $$);
+       }
+;
+
+%%
+
+static void
+add_macro_arg(const char *argtext, int argnum)
+{
+       struct macro_arg *marg;
+       int i;
+
+       if (macro_symbol == NULL || macro_symbol->type != MACRO) {
+               stop("Invalid current symbol for adding macro arg",
+                    EX_SOFTWARE);
+               /* NOTREACHED */
+       }
+       /*
+        * Macro Invocation.  Find the appropriate argument and fill
+        * in the replace ment text for this call.
+        */
+       i = 0;
+       STAILQ_FOREACH(marg, &macro_symbol->info.macroinfo->args, links) {
+               i++;
+               if (i == argnum)
+                       break;
+       }
+       if (marg == NULL) {
+               stop("Too many arguments for macro invocation", EX_DATAERR);
+               /* NOTREACHED */
+       }
+       marg->replacement_text = strdup(argtext);
+       if (marg->replacement_text == NULL) {
+               stop("Unable to replicate replacement text", EX_SOFTWARE);
+               /* NOTREACHED */
+       }
+}
+
+void
+mmerror(const char *string)
+{
+       stop(string, EX_DATAERR);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_macro_scan.l
new file mode 100644 (file)
index 0000000..c1bf57e
--- /dev/null
@@ -0,0 +1,154 @@
+%{
+/*
+ * Sub-Lexical Analyzer for macro invokation in 
+ * the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id$
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_macro_scan.l,v 1.1.2.1 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_macro_gram.h"
+
+#define MAX_STR_CONST 4096
+static char string_buf[MAX_STR_CONST];
+static char *string_buf_ptr;
+static int  parren_count;
+static char buf[255];
+%}
+
+WORD           [A-Za-z_][-A-Za-z_0-9]*
+SPACE          [ \t]+
+MCARG          [^(), \t]+
+
+%x ARGLIST
+
+%%
+\n                     {
+                               ++yylineno;
+                       }
+<ARGLIST>{SPACE}       ;
+<ARGLIST>\(            {
+                               parren_count++;
+                               if (parren_count == 1) {
+                                       string_buf_ptr = string_buf;
+                                       return ('(');
+                               }
+                               *string_buf_ptr++ = '(';
+                       }
+<ARGLIST>\)            {
+                               if (parren_count == 1) {
+                                       if (string_buf_ptr != string_buf) {
+                                               /*
+                                                * Return an argument and
+                                                * rescan this parren so we
+                                                * can return it as well.
+                                                */
+                                               *string_buf_ptr = '\0';
+                                               mmlval.str = string_buf;
+                                               string_buf_ptr = string_buf;
+                                               unput(')');
+                                               return T_ARG;
+                                       }
+                                       BEGIN INITIAL;
+                                       return (')');
+                               }
+                               parren_count--;
+                               *string_buf_ptr++ = ')';
+                       }
+<ARGLIST>{MCARG}       {
+                               char *yptr;
+
+                               yptr = mmtext;
+                               while (*yptr)
+                                       *string_buf_ptr++ = *yptr++;
+                       }
+<ARGLIST>\,            {
+                               if (string_buf_ptr != string_buf) {
+                                       /*
+                                        * Return an argument and
+                                        * rescan this comma so we
+                                        * can return it as well.
+                                        */
+                                       *string_buf_ptr = '\0';
+                                       mmlval.str = string_buf;
+                                       string_buf_ptr = string_buf;
+                                       unput(',');
+                                       return T_ARG;
+                               }
+                               return ',';
+                       }
+{WORD}[(]              {
+                               /* May be a symbol or a macro invocation. */
+                               mmlval.sym = symtable_get(mmtext);
+                               if (mmlval.sym->type != MACRO) {
+                                       stop("Expecting Macro Name",
+                                            EX_DATAERR);
+                               }
+                               unput('(');
+                               parren_count = 0;
+                               BEGIN ARGLIST;
+                               return T_SYMBOL;
+                       }
+.                      { 
+                               snprintf(buf, sizeof(buf), "Invalid character "
+                                        "'%c'", mmtext[0]);
+                               stop(buf, EX_DATAERR);
+                       }
+%%
+
+int
+mmwrap()
+{
+       stop("EOF encountered in macro call", EX_DATAERR);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
new file mode 100644 (file)
index 0000000..024c576
--- /dev/null
@@ -0,0 +1,594 @@
+%{
+/*
+ * Lexical Analyzer for the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs.
+ * Copyright (c) 2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_scan.l#11 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_scan.l,v 1.13.2.4 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_gram.h"
+
+/* This is used for macro body capture too, so err on the large size. */
+#define MAX_STR_CONST 4096
+static char string_buf[MAX_STR_CONST];
+static char *string_buf_ptr;
+static int  parren_count;
+static int  quote_count;
+static char buf[255];
+%}
+
+PATH           ([/]*[-A-Za-z0-9_.])+
+WORD           [A-Za-z_][-A-Za-z_0-9]*
+SPACE          [ \t]+
+MCARG          [^(), \t]+
+MBODY          ((\\[^\n])*[^\n\\]*)+
+
+%x COMMENT
+%x CEXPR
+%x INCLUDE
+%x STRING
+%x MACRODEF
+%x MACROARGLIST
+%x MACROCALLARGS
+%x MACROBODY
+
+%%
+\n                     { ++yylineno; }
+"/*"                   { BEGIN COMMENT;  /* Enter comment eating state */ }
+<COMMENT>"/*"          { fprintf(stderr, "Warning! Comment within comment."); }
+<COMMENT>\n            { ++yylineno; }
+<COMMENT>[^*/\n]*      ;
+<COMMENT>"*"+[^*/\n]*  ;
+<COMMENT>"/"+[^*/\n]*  ;
+<COMMENT>"*"+"/"       { BEGIN INITIAL; }
+if[ \t]*\(             {
+                               string_buf_ptr = string_buf;
+                               parren_count = 1;
+                               BEGIN CEXPR;
+                               return T_IF;
+                       }
+<CEXPR>\(              {       *string_buf_ptr++ = '('; parren_count++; }
+<CEXPR>\)              {
+                               parren_count--;
+                               if (parren_count == 0) {
+                                       /* All done */
+                                       BEGIN INITIAL;
+                                       *string_buf_ptr = '\0';
+                                       yylval.sym = symtable_get(string_buf);
+                                       return T_CEXPR;
+                               } else {
+                                       *string_buf_ptr++ = ')';
+                               }
+                       }
+<CEXPR>\n              { ++yylineno; }
+<CEXPR>[^()\n]+        {
+                               char *yptr;
+
+                               yptr = yytext;
+                               while (*yptr != '\0') {
+                                       /* Remove duplicate spaces */
+                                       if (*yptr == '\t')
+                                               *yptr = ' ';
+                                       if (*yptr == ' '
+                                        && string_buf_ptr != string_buf
+                                        && string_buf_ptr[-1] == ' ')
+                                               yptr++;
+                                       else 
+                                               *string_buf_ptr++ = *yptr++;
+                               }
+                       }
+
+VERSION                        { return T_VERSION; }
+PATCH_ARG_LIST         { return T_PATCH_ARG_LIST; }
+\"                     {
+                               string_buf_ptr = string_buf;
+                               BEGIN STRING;
+                       }
+<STRING>[^"]+          {
+                               char *yptr;
+
+                               yptr = yytext;
+                               while (*yptr)
+                                       *string_buf_ptr++ = *yptr++;
+                       }
+<STRING>\"             {
+                               /* All done */
+                               BEGIN INITIAL;
+                               *string_buf_ptr = '\0';
+                               yylval.str = string_buf;
+                               return T_STRING;
+                       }
+{SPACE}                         ;
+
+       /* Register/SCB/SRAM definition keywords */
+export                 { return T_EXPORT; }
+register               { return T_REGISTER; }
+const                  { yylval.value = FALSE; return T_CONST; }
+download               { return T_DOWNLOAD; }
+address                        { return T_ADDRESS; }
+access_mode            { return T_ACCESS_MODE; }
+modes                  { return T_MODES; }
+RW|RO|WO               {
+                                if (strcmp(yytext, "RW") == 0)
+                                       yylval.value = RW;
+                                else if (strcmp(yytext, "RO") == 0)
+                                       yylval.value = RO;
+                                else
+                                       yylval.value = WO;
+                                return T_MODE;
+                       }
+BEGIN_CRITICAL         { return T_BEGIN_CS; }
+END_CRITICAL           { return T_END_CS; }
+SET_SRC_MODE           { return T_SET_SRC_MODE; }
+SET_DST_MODE           { return T_SET_DST_MODE; }
+bit                    { return T_BIT; }
+mask                   { return T_MASK; }
+alias                  { return T_ALIAS; }
+size                   { return T_SIZE; }
+scb                    { return T_SCB; }
+scratch_ram            { return T_SRAM; }
+accumulator            { return T_ACCUM; }
+mode_pointer           { return T_MODE_PTR; }
+allones                        { return T_ALLONES; }
+allzeros               { return T_ALLZEROS; }
+none                   { return T_NONE; }
+sindex                 { return T_SINDEX; }
+A                      { return T_A; }
+
+       /* Opcodes */
+shl                    { return T_SHL; }
+shr                    { return T_SHR; }
+ror                    { return T_ROR; }
+rol                    { return T_ROL; }
+mvi                    { return T_MVI; }
+mov                    { return T_MOV; }
+clr                    { return T_CLR; }
+jmp                    { return T_JMP; }
+jc                     { return T_JC;  }
+jnc                    { return T_JNC; }
+je                     { return T_JE;  }
+jne                    { return T_JNE; }
+jz                     { return T_JZ;  }
+jnz                    { return T_JNZ; }
+call                   { return T_CALL; }
+add                    { return T_ADD; }
+adc                    { return T_ADC; }
+bmov                   { return T_BMOV; }
+inc                    { return T_INC; }
+dec                    { return T_DEC; }
+stc                    { return T_STC; }
+clc                    { return T_CLC; }
+cmp                    { return T_CMP; }
+not                    { return T_NOT; }
+xor                    { return T_XOR; }
+test                   { return T_TEST;}
+and                    { return T_AND; }
+or                     { return T_OR;  }
+ret                    { return T_RET; }
+nop                    { return T_NOP; }
+else                   { return T_ELSE; }
+
+       /* Allowed Symbols */
+\<\<                   { return T_EXPR_LSHIFT; }
+\>\>                   { return T_EXPR_RSHIFT; }
+[-+,:()~|&."{};<>[\]/*!=] { return yytext[0]; }
+
+       /* Number processing */
+0[0-7]*                        {
+                               yylval.value = strtol(yytext, NULL, 8);
+                               return T_NUMBER;
+                       }
+
+0[xX][0-9a-fA-F]+      {
+                               yylval.value = strtoul(yytext + 2, NULL, 16);
+                               return T_NUMBER;
+                       }
+
+[1-9][0-9]*            {
+                               yylval.value = strtol(yytext, NULL, 10);
+                               return T_NUMBER;
+                       }
+       /* Include Files */
+#include{SPACE}                {
+                               BEGIN INCLUDE;
+                               quote_count = 0;
+                               return T_INCLUDE;
+                       }
+<INCLUDE>[<]           { return yytext[0]; }
+<INCLUDE>[>]           { BEGIN INITIAL; return yytext[0]; }
+<INCLUDE>[\"]          {
+                               if (quote_count != 0)
+                                       BEGIN INITIAL;
+                               quote_count++;
+                               return yytext[0];
+                       }
+<INCLUDE>{PATH}                {
+                               char *yptr;
+
+                               yptr = yytext;
+                               string_buf_ptr = string_buf;
+                               while (*yptr)
+                                       *string_buf_ptr++ = *yptr++;
+                               yylval.str = string_buf;
+                               *string_buf_ptr = '\0';
+                               return T_PATH;
+                       }
+<INCLUDE>.             { stop("Invalid include line", EX_DATAERR); }
+#define{SPACE}         {
+                               BEGIN MACRODEF;
+                               return T_DEFINE;
+                       }
+<MACRODEF>{WORD}{SPACE}        { 
+                               char *yptr;
+
+                               /* Strip space and return as a normal symbol */
+                               yptr = yytext;
+                               while (*yptr != ' ' && *yptr != '\t')
+                                       yptr++;
+                               *yptr = '\0';
+                               yylval.sym = symtable_get(yytext);
+                               string_buf_ptr = string_buf;
+                               BEGIN MACROBODY;
+                               return T_SYMBOL;
+                       }
+<MACRODEF>{WORD}\(     {
+                               /*
+                                * We store the symbol with its opening
+                                * parren so we can differentiate macros
+                                * that take args from macros with the
+                                * same name that do not take args as
+                                * is allowed in C.
+                                */
+                               BEGIN MACROARGLIST;
+                               yylval.sym = symtable_get(yytext);
+                               unput('(');
+                               return T_SYMBOL;
+                       }
+<MACROARGLIST>{WORD}   {
+                               yylval.str = yytext;
+                               return T_ARG;
+                       }
+<MACROARGLIST>{SPACE}   ;
+<MACROARGLIST>[(,]     {
+                               return yytext[0];
+                       }
+<MACROARGLIST>[)]      {
+                               string_buf_ptr = string_buf;
+                               BEGIN MACROBODY;
+                               return ')';
+                       }
+<MACROARGLIST>.                {
+                               snprintf(buf, sizeof(buf), "Invalid character "
+                                        "'%c' in macro argument list",
+                                        yytext[0]);
+                               stop(buf, EX_DATAERR);
+                       }
+<MACROCALLARGS>{SPACE}  ;
+<MACROCALLARGS>\(      {
+                               parren_count++;
+                               if (parren_count == 1)
+                                       return ('(');
+                               *string_buf_ptr++ = '(';
+                       }
+<MACROCALLARGS>\)      {
+                               parren_count--;
+                               if (parren_count == 0) {
+                                       BEGIN INITIAL;
+                                       return (')');
+                               }
+                               *string_buf_ptr++ = ')';
+                       }
+<MACROCALLARGS>{MCARG} {
+                               char *yptr;
+
+                               yptr = yytext;
+                               while (*yptr)
+                                       *string_buf_ptr++ = *yptr++;
+                       }
+<MACROCALLARGS>\,      {
+                               if (string_buf_ptr != string_buf) {
+                                       /*
+                                        * Return an argument and
+                                        * rescan this comma so we
+                                        * can return it as well.
+                                        */
+                                       *string_buf_ptr = '\0';
+                                       yylval.str = string_buf;
+                                       string_buf_ptr = string_buf;
+                                       unput(',');
+                                       return T_ARG;
+                               }
+                               return ',';
+                       }
+<MACROBODY>\\\n                {
+                               /* Eat escaped newlines. */
+                               ++yylineno;
+                       }
+<MACROBODY>\n          {
+                               /* Macros end on the first unescaped newline. */
+                               BEGIN INITIAL;
+                               *string_buf_ptr = '\0';
+                               yylval.str = string_buf;
+                               ++yylineno;
+                               return T_MACROBODY;
+                       }
+<MACROBODY>{MBODY}     {
+                               char *yptr;
+
+                               yptr = yytext;
+                               while (*yptr)
+                                       *string_buf_ptr++ = *yptr++;
+                       }
+{WORD}\(               {
+                               char *yptr;
+                               char *ycopy;
+
+                               /* May be a symbol or a macro invocation. */
+                               yylval.sym = symtable_get(yytext);
+                               if (yylval.sym->type == MACRO) {
+                                       YY_BUFFER_STATE old_state;
+                                       YY_BUFFER_STATE temp_state;
+
+                                       ycopy = strdup(yytext);
+                                       yptr = ycopy + yyleng;
+                                       while (yptr > ycopy)
+                                               unput(*--yptr);
+                                       old_state = YY_CURRENT_BUFFER;
+                                       temp_state =
+                                           yy_create_buffer(stdin,
+                                                            YY_BUF_SIZE);
+                                       yy_switch_to_buffer(temp_state);
+                                       mm_switch_to_buffer(old_state);
+                                       mmparse();
+                                       mm_switch_to_buffer(temp_state);
+                                       yy_switch_to_buffer(old_state);
+                                       mm_delete_buffer(temp_state);
+                                       expand_macro(yylval.sym);
+                               } else {
+                                       if (yylval.sym->type == UNINITIALIZED) {
+                                               /* Try without the '(' */
+                                               symbol_delete(yylval.sym);
+                                               yytext[yyleng-1] = '\0';
+                                               yylval.sym =
+                                                   symtable_get(yytext);
+                                       }
+                                       unput('(');
+                                       return T_SYMBOL;
+                               }
+                       }
+{WORD}                 {
+                               yylval.sym = symtable_get(yytext);
+                               if (yylval.sym->type == MACRO) {
+                                       expand_macro(yylval.sym);
+                               } else {
+                                       return T_SYMBOL;
+                               }
+                       }
+.                      { 
+                               snprintf(buf, sizeof(buf), "Invalid character "
+                                        "'%c'", yytext[0]);
+                               stop(buf, EX_DATAERR);
+                       }
+%%
+
+typedef struct include {
+        YY_BUFFER_STATE  buffer;
+        int              lineno;
+        char            *filename;
+       SLIST_ENTRY(include) links;
+}include_t;
+
+SLIST_HEAD(, include) include_stack;
+
+void
+include_file(char *file_name, include_type type)
+{
+       FILE *newfile;
+       include_t *include;
+
+       newfile = NULL;
+       /* Try the current directory first */
+       if (includes_search_curdir != 0 || type == SOURCE_FILE)
+               newfile = fopen(file_name, "r");
+
+       if (newfile == NULL && type != SOURCE_FILE) {
+                path_entry_t include_dir;
+                for (include_dir = search_path.slh_first;
+                     include_dir != NULL;                
+                     include_dir = include_dir->links.sle_next) {
+                       char fullname[PATH_MAX];
+
+                       if ((include_dir->quoted_includes_only == TRUE)
+                        && (type != QUOTED_INCLUDE))
+                               continue;
+
+                       snprintf(fullname, sizeof(fullname),
+                                "%s/%s", include_dir->directory, file_name);
+
+                       if ((newfile = fopen(fullname, "r")) != NULL)
+                               break;
+                }
+        }
+
+       if (newfile == NULL) {
+               perror(file_name);
+               stop("Unable to open input file", EX_SOFTWARE);
+               /* NOTREACHED */
+       }
+
+       if (type != SOURCE_FILE) {
+               include = (include_t *)malloc(sizeof(include_t));
+               if (include == NULL) {
+                       stop("Unable to allocate include stack entry",
+                            EX_SOFTWARE);
+                       /* NOTREACHED */
+               }
+               include->buffer = YY_CURRENT_BUFFER;
+               include->lineno = yylineno;
+               include->filename = yyfilename;
+               SLIST_INSERT_HEAD(&include_stack, include, links);
+       }
+       yy_switch_to_buffer(yy_create_buffer(newfile, YY_BUF_SIZE));
+       yylineno = 1;
+       yyfilename = strdup(file_name);
+}
+
+static void next_substitution(struct symbol *mac_symbol, const char *body_pos,
+                             const char **next_match,
+                             struct macro_arg **match_marg, regmatch_t *match);
+
+void
+expand_macro(struct symbol *macro_symbol)
+{
+       struct macro_arg *marg;
+       struct macro_arg *match_marg;
+       const char *body_head;
+       const char *body_pos;
+       const char *next_match;
+
+       /*
+        * Due to the nature of unput, we must work
+        * backwards through the macro body performing
+        * any expansions.
+        */
+       body_head = macro_symbol->info.macroinfo->body;
+       body_pos = body_head + strlen(body_head);
+       while (body_pos > body_head) {
+               regmatch_t match;
+
+               next_match = body_head;
+               match_marg = NULL;
+               next_substitution(macro_symbol, body_pos, &next_match,
+                                 &match_marg, &match);
+
+               /* Put back everything up until the replacement. */
+               while (body_pos > next_match)
+                       unput(*--body_pos);
+
+               /* Perform the replacement. */
+               if (match_marg != NULL) {
+                       const char *strp;
+
+                       next_match = match_marg->replacement_text;
+                       strp = next_match + strlen(next_match);
+                       while (strp > next_match)
+                               unput(*--strp);
+
+                       /* Skip past the unexpanded macro arg. */
+                       body_pos -= match.rm_eo - match.rm_so;
+               }
+       }
+
+       /* Cleanup replacement text. */
+       STAILQ_FOREACH(marg, &macro_symbol->info.macroinfo->args, links) {
+               free(marg->replacement_text);
+       }
+}
+
+/*
+ * Find the next substitution in the macro working backwards from
+ * body_pos until the beginning of the macro buffer.  next_match
+ * should be initialized to the beginning of the macro buffer prior
+ * to calling this routine.
+ */
+static void
+next_substitution(struct symbol *mac_symbol, const char *body_pos,
+                 const char **next_match, struct macro_arg **match_marg,
+                 regmatch_t *match)
+{
+       regmatch_t        matches[2];
+       struct macro_arg *marg;
+       const char       *search_pos;
+       int               retval;
+
+       do {
+               search_pos = *next_match;
+
+               STAILQ_FOREACH(marg, &mac_symbol->info.macroinfo->args, links) {
+
+                       retval = regexec(&marg->arg_regex, search_pos, 2,
+                                        matches, 0);
+                       if (retval == 0
+                        && (matches[1].rm_eo + search_pos) <= body_pos
+                        && (matches[1].rm_eo + search_pos) > *next_match) {
+                               *match = matches[1];
+                               *next_match = match->rm_eo + search_pos;
+                               *match_marg = marg;
+                       }
+               }
+       } while (search_pos != *next_match);
+}
+
+int
+yywrap()
+{
+       include_t *include;
+
+       yy_delete_buffer(YY_CURRENT_BUFFER);
+       (void)fclose(yyin);
+       if (yyfilename != NULL)
+               free(yyfilename);
+       yyfilename = NULL;
+       include = include_stack.slh_first;
+       if (include != NULL) {
+               yy_switch_to_buffer(include->buffer);
+               yylineno = include->lineno;
+               yyfilename = include->filename;
+               SLIST_REMOVE_HEAD(&include_stack, links);
+               free(include);
+               return (0);
+       }
+       return (1);
+}
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
new file mode 100644 (file)
index 0000000..acb3560
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler symbol table implementation
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_symbol.c#14 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_symbol.c,v 1.11.2.4 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+#include <sys/types.h>
+
+#ifdef __linux__
+#include "aicdb.h"
+#else
+#include <db.h>
+#endif
+#include <fcntl.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "aicasm_symbol.h"
+#include "aicasm.h"
+
+static DB *symtable;
+
+symbol_t *
+symbol_create(char *name)
+{
+       symbol_t *new_symbol;
+
+       new_symbol = (symbol_t *)malloc(sizeof(symbol_t));
+       if (new_symbol == NULL) {
+               perror("Unable to create new symbol");
+               exit(EX_SOFTWARE);
+       }
+       memset(new_symbol, 0, sizeof(*new_symbol));
+       new_symbol->name = strdup(name);
+       new_symbol->type = UNINITIALIZED;
+       return (new_symbol);
+}
+
+void
+symbol_delete(symbol_t *symbol)
+{
+       if (symtable != NULL) {
+               DBT      key;
+
+               key.data = symbol->name;
+               key.size = strlen(symbol->name);
+               symtable->del(symtable, &key, /*flags*/0);
+       }
+       switch(symbol->type) {
+       case SCBLOC:
+       case SRAMLOC:
+       case REGISTER:
+               if (symbol->info.rinfo != NULL)
+                       free(symbol->info.rinfo);
+               break;
+       case ALIAS:
+               if (symbol->info.ainfo != NULL)
+                       free(symbol->info.ainfo);
+               break;
+       case MASK:
+       case BIT:
+               if (symbol->info.minfo != NULL) {
+                       symlist_free(&symbol->info.minfo->symrefs);
+                       free(symbol->info.minfo);
+               }
+               break;
+       case DOWNLOAD_CONST:
+       case CONST:
+               if (symbol->info.cinfo != NULL)
+                       free(symbol->info.cinfo);
+               break;
+       case LABEL:
+               if (symbol->info.linfo != NULL)
+                       free(symbol->info.linfo);
+               break;
+       case UNINITIALIZED:
+       default:
+               break;
+       }
+       free(symbol->name);
+       free(symbol);
+}
+
+void
+symtable_open()
+{
+       symtable = dbopen(/*filename*/NULL,
+                         O_CREAT | O_NONBLOCK | O_RDWR, /*mode*/0, DB_HASH,
+                         /*openinfo*/NULL);
+
+       if (symtable == NULL) {
+               perror("Symbol table creation failed");
+               exit(EX_SOFTWARE);
+               /* NOTREACHED */
+       }
+}
+
+void
+symtable_close()
+{
+       if (symtable != NULL) {
+               DBT      key;
+               DBT      data;
+
+               while (symtable->seq(symtable, &key, &data, R_FIRST) == 0) {
+                       symbol_t *stored_ptr;
+
+                       memcpy(&stored_ptr, data.data, sizeof(stored_ptr));
+                       symbol_delete(stored_ptr);
+               }
+               symtable->close(symtable);
+       }
+}
+
+/*
+ * The semantics of get is to return an uninitialized symbol entry
+ * if a lookup fails.
+ */
+symbol_t *
+symtable_get(char *name)
+{
+       symbol_t *stored_ptr;
+       DBT       key;
+       DBT       data;
+       int       retval;
+
+       key.data = (void *)name;
+       key.size = strlen(name);
+
+       if ((retval = symtable->get(symtable, &key, &data, /*flags*/0)) != 0) {
+               if (retval == -1) {
+                       perror("Symbol table get operation failed");
+                       exit(EX_SOFTWARE);
+                       /* NOTREACHED */
+               } else if (retval == 1) {
+                       /* Symbol wasn't found, so create a new one */
+                       symbol_t *new_symbol;
+
+                       new_symbol = symbol_create(name);
+                       data.data = &new_symbol;
+                       data.size = sizeof(new_symbol);
+                       if (symtable->put(symtable, &key, &data,
+                                         /*flags*/0) !=0) {
+                               perror("Symtable put failed");
+                               exit(EX_SOFTWARE);
+                       }
+                       return (new_symbol);
+               } else {
+                       perror("Unexpected return value from db get routine");
+                       exit(EX_SOFTWARE);
+                       /* NOTREACHED */
+               }
+       }
+       memcpy(&stored_ptr, data.data, sizeof(stored_ptr));
+       return (stored_ptr);
+}
+
+symbol_node_t *
+symlist_search(symlist_t *symlist, char *symname)
+{
+       symbol_node_t *curnode;
+
+       curnode = SLIST_FIRST(symlist);
+       while(curnode != NULL) {
+               if (strcmp(symname, curnode->symbol->name) == 0)
+                       break;
+               curnode = SLIST_NEXT(curnode, links);
+       }
+       return (curnode);
+}
+
+void
+symlist_add(symlist_t *symlist, symbol_t *symbol, int how)
+{
+       symbol_node_t *newnode;
+
+       newnode = (symbol_node_t *)malloc(sizeof(symbol_node_t));
+       if (newnode == NULL) {
+               stop("symlist_add: Unable to malloc symbol_node", EX_SOFTWARE);
+               /* NOTREACHED */
+       }
+       newnode->symbol = symbol;
+       if (how == SYMLIST_SORT) {
+               symbol_node_t *curnode;
+               int mask;
+
+               mask = FALSE;
+               switch(symbol->type) {
+               case REGISTER:
+               case SCBLOC:
+               case SRAMLOC:
+                       break;
+               case BIT:
+               case MASK:
+                       mask = TRUE;
+                       break;
+               default:
+                       stop("symlist_add: Invalid symbol type for sorting",
+                            EX_SOFTWARE);
+                       /* NOTREACHED */
+               }
+
+               curnode = SLIST_FIRST(symlist);
+               if (curnode == NULL
+                || (mask && (curnode->symbol->info.minfo->mask >
+                             newnode->symbol->info.minfo->mask))
+                || (!mask && (curnode->symbol->info.rinfo->address >
+                              newnode->symbol->info.rinfo->address))) {
+                       SLIST_INSERT_HEAD(symlist, newnode, links);
+                       return;
+               }
+
+               while (1) {
+                       if (SLIST_NEXT(curnode, links) == NULL) {
+                               SLIST_INSERT_AFTER(curnode, newnode,
+                                                  links);
+                               break;
+                       } else {
+                               symbol_t *cursymbol;
+
+                               cursymbol = SLIST_NEXT(curnode, links)->symbol;
+                               if ((mask && (cursymbol->info.minfo->mask >
+                                             symbol->info.minfo->mask))
+                                || (!mask &&(cursymbol->info.rinfo->address >
+                                             symbol->info.rinfo->address))){
+                                       SLIST_INSERT_AFTER(curnode, newnode,
+                                                          links);
+                                       break;
+                               }
+                       }
+                       curnode = SLIST_NEXT(curnode, links);
+               }
+       } else {
+               SLIST_INSERT_HEAD(symlist, newnode, links);
+       }
+}
+
+void
+symlist_free(symlist_t *symlist)
+{
+       symbol_node_t *node1, *node2;
+
+       node1 = SLIST_FIRST(symlist);
+       while (node1 != NULL) {
+               node2 = SLIST_NEXT(node1, links);
+               free(node1);
+               node1 = node2;
+       }
+       SLIST_INIT(symlist);
+}
+
+void
+symlist_merge(symlist_t *symlist_dest, symlist_t *symlist_src1,
+             symlist_t *symlist_src2)
+{
+       symbol_node_t *node;
+
+       *symlist_dest = *symlist_src1;
+       while((node = SLIST_FIRST(symlist_src2)) != NULL) {
+               SLIST_REMOVE_HEAD(symlist_src2, links);
+               SLIST_INSERT_HEAD(symlist_dest, node, links);
+       }
+
+       /* These are now empty */
+       SLIST_INIT(symlist_src1);
+       SLIST_INIT(symlist_src2);
+}
+
+void
+symtable_dump(FILE *ofile)
+{
+       /*
+        * Sort the registers by address with a simple insertion sort.
+        * Put bitmasks next to the first register that defines them.
+        * Put constants at the end.
+        */
+       symlist_t registers;
+       symlist_t masks;
+       symlist_t constants;
+       symlist_t download_constants;
+       symlist_t aliases;
+       symlist_t exported_labels;
+       u_int     i;
+
+       SLIST_INIT(&registers);
+       SLIST_INIT(&masks);
+       SLIST_INIT(&constants);
+       SLIST_INIT(&download_constants);
+       SLIST_INIT(&aliases);
+       SLIST_INIT(&exported_labels);
+
+       if (symtable != NULL) {
+               DBT      key;
+               DBT      data;
+               int      flag = R_FIRST;
+
+               while (symtable->seq(symtable, &key, &data, flag) == 0) {
+                       symbol_t *cursym;
+
+                       memcpy(&cursym, data.data, sizeof(cursym));
+                       switch(cursym->type) {
+                       case REGISTER:
+                       case SCBLOC:
+                       case SRAMLOC:
+                               symlist_add(&registers, cursym, SYMLIST_SORT);
+                               break;
+                       case MASK:
+                       case BIT:
+                               symlist_add(&masks, cursym, SYMLIST_SORT);
+                               break;
+                       case CONST:
+                               symlist_add(&constants, cursym,
+                                           SYMLIST_INSERT_HEAD);
+                               break;
+                       case DOWNLOAD_CONST:
+                               symlist_add(&download_constants, cursym,
+                                           SYMLIST_INSERT_HEAD);
+                               break;
+                       case ALIAS:
+                               symlist_add(&aliases, cursym,
+                                           SYMLIST_INSERT_HEAD);
+                               break;
+                       case LABEL:
+                               if (cursym->info.linfo->exported == 0)
+                                       break;
+                               symlist_add(&exported_labels, cursym,
+                                           SYMLIST_INSERT_HEAD);
+                               break;
+                       default:
+                               break;
+                       }
+                       flag = R_NEXT;
+               }
+
+               /* Put in the masks and bits */
+               while (SLIST_FIRST(&masks) != NULL) {
+                       symbol_node_t *curnode;
+                       symbol_node_t *regnode;
+                       char *regname;
+
+                       curnode = SLIST_FIRST(&masks);
+                       SLIST_REMOVE_HEAD(&masks, links);
+
+                       regnode =
+                           SLIST_FIRST(&curnode->symbol->info.minfo->symrefs);
+                       regname = regnode->symbol->name;
+                       regnode = symlist_search(&registers, regname);
+                       SLIST_INSERT_AFTER(regnode, curnode, links);
+               }
+
+               /* Add the aliases */
+               while (SLIST_FIRST(&aliases) != NULL) {
+                       symbol_node_t *curnode;
+                       symbol_node_t *regnode;
+                       char *regname;
+
+                       curnode = SLIST_FIRST(&aliases);
+                       SLIST_REMOVE_HEAD(&aliases, links);
+
+                       regname = curnode->symbol->info.ainfo->parent->name;
+                       regnode = symlist_search(&registers, regname);
+                       SLIST_INSERT_AFTER(regnode, curnode, links);
+               }
+
+               /* Output what we have */
+               fprintf(ofile,
+"/*
+ * DO NOT EDIT - This file is automatically generated
+ *              from the following source files:
+ *
+%s */\n", versions);
+               while (SLIST_FIRST(&registers) != NULL) {
+                       symbol_node_t *curnode;
+                       u_int value;
+                       char *tab_str;
+                       char *tab_str2;
+
+                       curnode = SLIST_FIRST(&registers);
+                       SLIST_REMOVE_HEAD(&registers, links);
+                       switch(curnode->symbol->type) {
+                       case REGISTER:
+                       case SCBLOC:
+                       case SRAMLOC:
+                               fprintf(ofile, "\n");
+                               value = curnode->symbol->info.rinfo->address;
+                               tab_str = "\t";
+                               tab_str2 = "\t\t";
+                               break;
+                       case ALIAS:
+                       {
+                               symbol_t *parent;
+
+                               parent = curnode->symbol->info.ainfo->parent;
+                               value = parent->info.rinfo->address;
+                               tab_str = "\t";
+                               tab_str2 = "\t\t";
+                               break;
+                       }
+                       case MASK:
+                       case BIT:
+                               value = curnode->symbol->info.minfo->mask;
+                               tab_str = "\t\t";
+                               tab_str2 = "\t";
+                               break;
+                       default:
+                               value = 0; /* Quiet compiler */
+                               tab_str = NULL;
+                               tab_str2 = NULL;
+                               stop("symtable_dump: Invalid symbol type "
+                                    "encountered", EX_SOFTWARE);
+                               break;
+                       }
+                       fprintf(ofile, "#define%s%-16s%s0x%02x\n",
+                               tab_str, curnode->symbol->name, tab_str2,
+                               value);
+                       free(curnode);
+               }
+               fprintf(ofile, "\n\n");
+
+               while (SLIST_FIRST(&constants) != NULL) {
+                       symbol_node_t *curnode;
+
+                       curnode = SLIST_FIRST(&constants);
+                       SLIST_REMOVE_HEAD(&constants, links);
+                       fprintf(ofile, "#define\t%-8s\t0x%02x\n",
+                               curnode->symbol->name,
+                               curnode->symbol->info.cinfo->value);
+                       free(curnode);
+               }
+
+               
+               fprintf(ofile, "\n\n/* Downloaded Constant Definitions */\n");
+
+               for (i = 0; SLIST_FIRST(&download_constants) != NULL; i++) {
+                       symbol_node_t *curnode;
+
+                       curnode = SLIST_FIRST(&download_constants);
+                       SLIST_REMOVE_HEAD(&download_constants, links);
+                       fprintf(ofile, "#define\t%-8s\t0x%02x\n",
+                               curnode->symbol->name,
+                               curnode->symbol->info.cinfo->value);
+                       free(curnode);
+               }
+               fprintf(ofile, "#define\tDOWNLOAD_CONST_COUNT\t0x%02x\n", i);
+
+               fprintf(ofile, "\n\n/* Exported Labels */\n");
+
+               while (SLIST_FIRST(&exported_labels) != NULL) {
+                       symbol_node_t *curnode;
+
+                       curnode = SLIST_FIRST(&exported_labels);
+                       SLIST_REMOVE_HEAD(&exported_labels, links);
+                       fprintf(ofile, "#define\tLABEL_%-8s\t0x%02x\n",
+                               curnode->symbol->name,
+                               curnode->symbol->info.linfo->address);
+                       free(curnode);
+               }
+       }
+}
+
diff --git a/xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h b/xen/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
new file mode 100644 (file)
index 0000000..1100c62
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler symbol table definitions
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aicasm/aicasm_symbol.h#11 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_symbol.h,v 1.11.2.3 2002/04/29 19:36:36 gibbs Exp $
+ */
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+typedef enum {
+       UNINITIALIZED,
+       REGISTER,
+       ALIAS,
+       SCBLOC,
+       SRAMLOC,
+       MASK,
+       BIT,
+       CONST,
+       DOWNLOAD_CONST,
+       LABEL,
+       CONDITIONAL,
+       MACRO
+} symtype;
+
+typedef enum {
+       RO = 0x01,
+       WO = 0x02,
+       RW = 0x03
+}amode_t;
+
+struct reg_info {
+       u_int    address;
+       int      size;
+       amode_t  mode;
+       u_int8_t valid_bitmask;
+       u_int8_t modes;
+       int      typecheck_masks;
+};
+
+typedef SLIST_HEAD(symlist, symbol_node) symlist_t;
+
+struct mask_info {
+       symlist_t symrefs;
+       u_int8_t mask;
+};
+
+struct const_info {
+       u_int   value;
+       int     define;
+};
+
+struct alias_info {
+       struct symbol *parent;
+};
+
+struct label_info {
+       int     address;
+       int     exported;
+};
+
+struct cond_info {
+       int     func_num;
+};
+
+struct macro_arg {
+       STAILQ_ENTRY(macro_arg) links;
+       regex_t arg_regex;
+       char   *replacement_text;
+};
+STAILQ_HEAD(macro_arg_list, macro_arg) args;
+
+struct macro_info {
+       struct macro_arg_list args;
+       int   narg;
+       const char* body;
+};
+
+typedef struct expression_info {
+        symlist_t       referenced_syms;
+        int             value;
+} expression_t;
+
+typedef struct symbol {
+       char    *name;
+       symtype type;
+       union   {
+               struct reg_info   *rinfo;
+               struct mask_info  *minfo;
+               struct const_info *cinfo;
+               struct alias_info *ainfo;
+               struct label_info *linfo;
+               struct cond_info  *condinfo;
+               struct macro_info *macroinfo;
+       }info;
+} symbol_t;
+
+typedef struct symbol_ref {
+       symbol_t *symbol;
+       int      offset;
+} symbol_ref_t;
+
+typedef struct symbol_node {
+       SLIST_ENTRY(symbol_node) links;
+       symbol_t *symbol;
+} symbol_node_t;
+
+typedef struct critical_section {
+       TAILQ_ENTRY(critical_section) links;
+       int begin_addr;
+       int end_addr;
+} critical_section_t;
+
+typedef enum {
+       SCOPE_ROOT,
+       SCOPE_IF,
+       SCOPE_ELSE_IF,
+       SCOPE_ELSE
+} scope_type;
+
+typedef struct patch_info {
+       int skip_patch;
+       int skip_instr;
+} patch_info_t;
+
+typedef struct scope {
+       SLIST_ENTRY(scope) scope_stack_links;
+       TAILQ_ENTRY(scope) scope_links;
+       TAILQ_HEAD(, scope) inner_scope;
+       scope_type type;
+       int inner_scope_patches;
+       int begin_addr;
+        int end_addr;
+       patch_info_t patches[2];
+       int func_num;
+} scope_t;
+
+TAILQ_HEAD(cs_tailq, critical_section);
+SLIST_HEAD(scope_list, scope);
+TAILQ_HEAD(scope_tailq, scope);
+
+void   symbol_delete __P((symbol_t *symbol));
+
+void   symtable_open __P((void));
+
+void   symtable_close __P((void));
+
+symbol_t *
+       symtable_get __P((char *name));
+
+symbol_node_t *
+       symlist_search __P((symlist_t *symlist, char *symname));
+
+void
+       symlist_add __P((symlist_t *symlist, symbol_t *symbol, int how));
+#define SYMLIST_INSERT_HEAD    0x00
+#define SYMLIST_SORT           0x01
+
+void   symlist_free __P((symlist_t *symlist));
+
+void   symlist_merge __P((symlist_t *symlist_dest, symlist_t *symlist_src1,
+                          symlist_t *symlist_src2));
+void   symtable_dump __P((FILE *ofile));
diff --git a/xen/drivers/scsi/aic7xxx/cam.h b/xen/drivers/scsi/aic7xxx/cam.h
new file mode 100644 (file)
index 0000000..d7a526f
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Data structures and definitions for the CAM system.
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2000 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/cam.h#11 $
+ */
+
+#ifndef _AIC7XXX_CAM_H
+#define _AIC7XXX_CAM_H 1
+
+/* Provide a mapping from CAM constructs to Linux SCSI constructs */
+
+#define        CAM_BUS_WILDCARD ((u_int)~0)
+#define        CAM_TARGET_WILDCARD ((u_int)~0)
+#define        CAM_LUN_WILDCARD ((u_int)~0)
+
+/* CAM Status field values */
+typedef enum {
+       /* CCB request is in progress */
+       CAM_REQ_INPROG          = 0x3F, /* Some value unused by Linux */
+       /* CCB request completed without error */
+       CAM_REQ_CMP             = DID_OK,
+       /* CCB request aborted by the host */
+       CAM_REQ_ABORTED         = DID_ABORT,
+       /* Unable to abort CCB request */
+       CAM_UA_ABORT            = DID_ERROR,
+       /* CCB request completed with an error */
+       CAM_REQ_CMP_ERR         = DID_ERROR,
+       /* CAM subsytem is busy */
+       CAM_BUSY                = DID_BUS_BUSY,
+       /* CCB request was invalid */
+       CAM_REQ_INVALID         = DID_BAD_TARGET,
+       /* Supplied Path ID is invalid */
+       CAM_PATH_INVALID        = DID_BAD_TARGET,
+       /* Target Selection Timeout */
+       CAM_SEL_TIMEOUT         = DID_NO_CONNECT,
+       /* Command timeout */
+       CAM_CMD_TIMEOUT         = DID_ERROR, /*
+                                             * Should never occur in Linux
+                                             * as the upper level code
+                                             * handles all timeout processing.
+                                             */
+       /* SCSI error, look at error code in CCB */
+       CAM_SCSI_STATUS_ERROR   = DID_OK, /* Linux looks at status byte */
+       /* SCSI Bus Reset Sent/Received */
+       CAM_SCSI_BUS_RESET      = DID_RESET,
+       /* Uncorrectable parity error occurred */
+       CAM_UNCOR_PARITY        = DID_PARITY,
+       /* Autosense: request sense cmd fail */
+       CAM_AUTOSENSE_FAIL      = DID_ERROR,
+       /* No HBA Detected Error */
+       CAM_NO_HBA              = DID_ERROR,
+       /* Data Overrun error */
+       CAM_DATA_RUN_ERR        = DID_ERROR,
+       /* Unexpected Bus Free */
+       CAM_UNEXP_BUSFREE       = DID_ERROR,
+       /* CCB length supplied is inadequate */
+       CAM_CCB_LEN_ERR         = DID_ERROR,
+       /* Unable to provide requested capability */
+       CAM_PROVIDE_FAIL        = DID_ERROR,
+       /* A SCSI BDR msg was sent to target */
+       CAM_BDR_SENT            = DID_RESET,
+       /* CCB request terminated by the host */
+       CAM_REQ_TERMIO          = DID_ERROR,
+       /* Unrecoverable Host Bus Adapter Error */
+       CAM_UNREC_HBA_ERROR     = DID_ERROR,
+       /* The request was too large for this host */
+       CAM_REQ_TOO_BIG         = DID_ERROR,
+       /*
+        * This request should be requeued to preserve
+        * transaction ordering.  This typically occurs
+        * when the SIM recognizes an error that should
+        * freeze the queue and must place additional
+        * requests for the target at the sim level
+        * back into the XPT queue.
+        */
+       CAM_REQUEUE_REQ         = DID_BUS_BUSY,
+
+       CAM_STATUS_MASK         = 0x3F
+} cam_status;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#define        SCSI_DATA_READ  1
+#define        SCSI_DATA_WRITE 2
+#define SCSI_DATA_NONE  3
+#endif
+
+/*
+ * Definitions for the asynchronous callback CCB fields.
+ */
+typedef enum {
+       AC_GETDEV_CHANGED       = 0x800,/* Getdev info might have changed */
+       AC_INQ_CHANGED          = 0x400,/* Inquiry info might have changed */
+       AC_TRANSFER_NEG         = 0x200,/* New transfer settings in effect */
+       AC_LOST_DEVICE          = 0x100,/* A device went away */
+       AC_FOUND_DEVICE         = 0x080,/* A new device was found */
+       AC_PATH_DEREGISTERED    = 0x040,/* A path has de-registered */
+       AC_PATH_REGISTERED      = 0x020,/* A new path has been registered */
+       AC_SENT_BDR             = 0x010,/* A BDR message was sent to target */
+       AC_SCSI_AEN             = 0x008,/* A SCSI AEN has been received */
+       AC_UNSOL_RESEL          = 0x002,/* Unsolicited reselection occurred */
+       AC_BUS_RESET            = 0x001 /* A SCSI bus reset occurred */
+} ac_code;
+
+typedef enum {
+       CAM_DIR_IN              = SCSI_DATA_READ,
+       CAM_DIR_OUT             = SCSI_DATA_WRITE,
+       CAM_DIR_NONE            = SCSI_DATA_NONE
+} ccb_flags;
+
+#endif /* _AIC7XXX_CAM_H */
diff --git a/xen/drivers/scsi/aic7xxx/queue.h b/xen/drivers/scsi/aic7xxx/queue.h
new file mode 100644 (file)
index 0000000..8adf800
--- /dev/null
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)queue.h     8.5 (Berkeley) 8/20/94
+ * $FreeBSD: src/sys/sys/queue.h,v 1.38 2000/05/26 02:06:56 jake Exp $
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define        _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ *                     SLIST   LIST    STAILQ  TAILQ   CIRCLEQ
+ * _HEAD               +       +       +       +       +
+ * _HEAD_INITIALIZER   +       +       +       +       +
+ * _ENTRY              +       +       +       +       +
+ * _INIT               +       +       +       +       +
+ * _EMPTY              +       +       +       +       +
+ * _FIRST              +       +       +       +       +
+ * _NEXT               +       +       +       +       +
+ * _PREV               -       -       -       +       +
+ * _LAST               -       -       +       +       +
+ * _FOREACH            +       +       +       +       +
+ * _FOREACH_REVERSE    -       -       -       +       +
+ * _INSERT_HEAD                +       +       +       +       +
+ * _INSERT_BEFORE      -       +       -       +       +
+ * _INSERT_AFTER       +       +       +       +       +
+ * _INSERT_TAIL                -       -       +       +       +
+ * _REMOVE_HEAD                +       -       +       -       -
+ * _REMOVE             +       +       +       +       +
+ *
+ */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define        SLIST_HEAD(name, type)                                          \
+struct name {                                                          \
+       struct type *slh_first; /* first element */                     \
+}
+
+#define        SLIST_HEAD_INITIALIZER(head)                                    \
+       { NULL }
+#define        SLIST_ENTRY(type)                                               \
+struct {                                                               \
+       struct type *sle_next;  /* next element */                      \
+}
+/*
+ * Singly-linked List functions.
+ */
+#define        SLIST_EMPTY(head)       ((head)->slh_first == NULL)
+
+#define        SLIST_FIRST(head)       ((head)->slh_first)
+
+#define        SLIST_FOREACH(var, head, field)                                 \
+       for ((var) = SLIST_FIRST((head));                               \
+           (var);                                                      \
+           (var) = SLIST_NEXT((var), field))
+
+#define        SLIST_INIT(head) do {                                           \
+       SLIST_FIRST((head)) = NULL;                                     \
+} while (0)
+
+#define        SLIST_INSERT_AFTER(slistelm, elm, field) do {                   \
+       SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field);       \
+       SLIST_NEXT((slistelm), field) = (elm);                          \
+} while (0)
+
+#define        SLIST_INSERT_HEAD(head, elm, field) do {                        \
+       SLIST_NEXT((elm), field) = SLIST_FIRST((head));                 \
+       SLIST_FIRST((head)) = (elm);                                    \
+} while (0)
+
+#define        SLIST_NEXT(elm, field)  ((elm)->field.sle_next)
+
+#define        SLIST_REMOVE(head, elm, type, field) do {                       \
+       if (SLIST_FIRST((head)) == (elm)) {                             \
+               SLIST_REMOVE_HEAD((head), field);                       \
+       }                                                               \
+       else {                                                          \
+               struct type *curelm = SLIST_FIRST((head));              \
+               while (SLIST_NEXT(curelm, field) != (elm))              \
+                       curelm = SLIST_NEXT(curelm, field);             \
+               SLIST_NEXT(curelm, field) =                             \
+                   SLIST_NEXT(SLIST_NEXT(curelm, field), field);       \
+       }                                                               \
+} while (0)
+
+#define        SLIST_REMOVE_HEAD(head, field) do {                             \
+       SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field);   \
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define        STAILQ_HEAD(name, type)                                         \
+struct name {                                                          \
+       struct type *stqh_first;/* first element */                     \
+       struct type **stqh_last;/* addr of last next element */         \
+}
+
+#define        STAILQ_HEAD_INITIALIZER(head)                                   \
+       { NULL, &(head).stqh_first }
+
+#define        STAILQ_ENTRY(type)                                              \
+struct {                                                               \
+       struct type *stqe_next; /* next element */                      \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define        STAILQ_EMPTY(head)      ((head)->stqh_first == NULL)
+
+#define        STAILQ_FIRST(head)      ((head)->stqh_first)
+
+#define        STAILQ_FOREACH(var, head, field)                                \
+       for((var) = STAILQ_FIRST((head));                               \
+          (var);                                                       \
+          (var) = STAILQ_NEXT((var), field))
+
+#define        STAILQ_INIT(head) do {                                          \
+       STAILQ_FIRST((head)) = NULL;                                    \
+       (head)->stqh_last = &STAILQ_FIRST((head));                      \
+} while (0)
+
+#define        STAILQ_INSERT_AFTER(head, tqelm, elm, field) do {               \
+       if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
+       STAILQ_NEXT((tqelm), field) = (elm);                            \
+} while (0)
+
+#define        STAILQ_INSERT_HEAD(head, elm, field) do {                       \
+       if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
+       STAILQ_FIRST((head)) = (elm);                                   \
+} while (0)
+
+#define        STAILQ_INSERT_TAIL(head, elm, field) do {                       \
+       STAILQ_NEXT((elm), field) = NULL;                               \
+       STAILQ_LAST((head)) = (elm);                                    \
+       (head)->stqh_last = &STAILQ_NEXT((elm), field);                 \
+} while (0)
+
+#define        STAILQ_LAST(head)       (*(head)->stqh_last)
+
+#define        STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define        STAILQ_REMOVE(head, elm, type, field) do {                      \
+       if (STAILQ_FIRST((head)) == (elm)) {                            \
+               STAILQ_REMOVE_HEAD(head, field);                        \
+       }                                                               \
+       else {                                                          \
+               struct type *curelm = STAILQ_FIRST((head));             \
+               while (STAILQ_NEXT(curelm, field) != (elm))             \
+                       curelm = STAILQ_NEXT(curelm, field);            \
+               if ((STAILQ_NEXT(curelm, field) =                       \
+                    STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
+                       (head)->stqh_last = &STAILQ_NEXT((curelm), field);\
+       }                                                               \
+} while (0)
+
+#define        STAILQ_REMOVE_HEAD(head, field) do {                            \
+       if ((STAILQ_FIRST((head)) =                                     \
+            STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL)         \
+               (head)->stqh_last = &STAILQ_FIRST((head));              \
+} while (0)
+
+#define        STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do {                 \
+       if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \
+               (head)->stqh_last = &STAILQ_FIRST((head));              \
+} while (0)
+
+/*
+ * List declarations.
+ */
+#define        LIST_HEAD(name, type)                                           \
+struct name {                                                          \
+       struct type *lh_first;  /* first element */                     \
+}
+
+#define        LIST_HEAD_INITIALIZER(head)                                     \
+       { NULL }
+
+#define        LIST_ENTRY(type)                                                \
+struct {                                                               \
+       struct type *le_next;   /* next element */                      \
+       struct type **le_prev;  /* address of previous next element */  \
+}
+
+/*
+ * List functions.
+ */
+
+#define        LIST_EMPTY(head)        ((head)->lh_first == NULL)
+
+#define        LIST_FIRST(head)        ((head)->lh_first)
+
+#define        LIST_FOREACH(var, head, field)                                  \
+       for ((var) = LIST_FIRST((head));                                \
+           (var);                                                      \
+           (var) = LIST_NEXT((var), field))
+
+#define        LIST_INIT(head) do {                                            \
+       LIST_FIRST((head)) = NULL;                                      \
+} while (0)
+
+#define        LIST_INSERT_AFTER(listelm, elm, field) do {                     \
+       if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+               LIST_NEXT((listelm), field)->field.le_prev =            \
+                   &LIST_NEXT((elm), field);                           \
+       LIST_NEXT((listelm), field) = (elm);                            \
+       (elm)->field.le_prev = &LIST_NEXT((listelm), field);            \
+} while (0)
+
+#define        LIST_INSERT_BEFORE(listelm, elm, field) do {                    \
+       (elm)->field.le_prev = (listelm)->field.le_prev;                \
+       LIST_NEXT((elm), field) = (listelm);                            \
+       *(listelm)->field.le_prev = (elm);                              \
+       (listelm)->field.le_prev = &LIST_NEXT((elm), field);            \
+} while (0)
+
+#define        LIST_INSERT_HEAD(head, elm, field) do {                         \
+       if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL)     \
+               LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+       LIST_FIRST((head)) = (elm);                                     \
+       (elm)->field.le_prev = &LIST_FIRST((head));                     \
+} while (0)
+
+#define        LIST_NEXT(elm, field)   ((elm)->field.le_next)
+
+#define        LIST_REMOVE(elm, field) do {                                    \
+       if (LIST_NEXT((elm), field) != NULL)                            \
+               LIST_NEXT((elm), field)->field.le_prev =                \
+                   (elm)->field.le_prev;                               \
+       *(elm)->field.le_prev = LIST_NEXT((elm), field);                \
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define        TAILQ_HEAD(name, type)                                          \
+struct name {                                                          \
+       struct type *tqh_first; /* first element */                     \
+       struct type **tqh_last; /* addr of last next element */         \
+}
+
+#define        TAILQ_HEAD_INITIALIZER(head)                                    \
+       { NULL, &(head).tqh_first }
+
+#define        TAILQ_ENTRY(type)                                               \
+struct {                                                               \
+       struct type *tqe_next;  /* next element */                      \
+       struct type **tqe_prev; /* address of previous next element */  \
+}
+
+/*
+ * Tail queue functions.
+ */
+#define        TAILQ_EMPTY(head)       ((head)->tqh_first == NULL)
+
+#define        TAILQ_FIRST(head)       ((head)->tqh_first)
+
+#define        TAILQ_FOREACH(var, head, field)                                 \
+       for ((var) = TAILQ_FIRST((head));                               \
+           (var);                                                      \
+           (var) = TAILQ_NEXT((var), field))
+
+#define        TAILQ_FOREACH_REVERSE(var, head, headname, field)               \
+       for ((var) = TAILQ_LAST((head), headname);                      \
+           (var);                                                      \
+           (var) = TAILQ_PREV((var), headname, field))
+
+#define        TAILQ_INIT(head) do {                                           \
+       TAILQ_FIRST((head)) = NULL;                                     \
+       (head)->tqh_last = &TAILQ_FIRST((head));                        \
+} while (0)
+
+#define        TAILQ_INSERT_AFTER(head, listelm, elm, field) do {              \
+       if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+               TAILQ_NEXT((elm), field)->field.tqe_prev =              \
+                   &TAILQ_NEXT((elm), field);                          \
+       else                                                            \
+               (head)->tqh_last = &TAILQ_NEXT((elm), field);           \
+       TAILQ_NEXT((listelm), field) = (elm);                           \
+       (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field);          \
+} while (0)
+
+#define        TAILQ_INSERT_BEFORE(listelm, elm, field) do {                   \
+       (elm)->field.tqe_prev = (listelm)->field.tqe_prev;              \
+       TAILQ_NEXT((elm), field) = (listelm);                           \
+       *(listelm)->field.tqe_prev = (elm);                             \
+       (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field);          \
+} while (0)
+
+#define        TAILQ_INSERT_HEAD(head, elm, field) do {                        \
+       if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL)   \
+               TAILQ_FIRST((head))->field.tqe_prev =                   \
+                   &TAILQ_NEXT((elm), field);                          \
+       else                                                            \
+               (head)->tqh_last = &TAILQ_NEXT((elm), field);           \
+       TAILQ_FIRST((head)) = (elm);                                    \
+       (elm)->field.tqe_prev = &TAILQ_FIRST((head));                   \
+} while (0)
+
+#define        TAILQ_INSERT_TAIL(head, elm, field) do {                        \
+       TAILQ_NEXT((elm), field) = NULL;                                \
+       (elm)->field.tqe_prev = (head)->tqh_last;                       \
+       *(head)->tqh_last = (elm);                                      \
+       (head)->tqh_last = &TAILQ_NEXT((elm), field);                   \
+} while (0)
+
+#define        TAILQ_LAST(head, headname)                                      \
+       (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define        TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define        TAILQ_PREV(elm, headname, field)                                \
+       (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define        TAILQ_REMOVE(head, elm, field) do {                             \
+       if ((TAILQ_NEXT((elm), field)) != NULL)                         \
+               TAILQ_NEXT((elm), field)->field.tqe_prev =              \
+                   (elm)->field.tqe_prev;                              \
+       else                                                            \
+               (head)->tqh_last = (elm)->field.tqe_prev;               \
+       *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);              \
+} while (0)
+
+/*
+ * Circular queue declarations.
+ */
+#define        CIRCLEQ_HEAD(name, type)                                        \
+struct name {                                                          \
+       struct type *cqh_first;         /* first element */             \
+       struct type *cqh_last;          /* last element */              \
+}
+
+#define        CIRCLEQ_HEAD_INITIALIZER(head)                                  \
+       { (void *)&(head), (void *)&(head) }
+
+#define        CIRCLEQ_ENTRY(type)                                             \
+struct {                                                               \
+       struct type *cqe_next;          /* next element */              \
+       struct type *cqe_prev;          /* previous element */          \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define        CIRCLEQ_EMPTY(head)     ((head)->cqh_first == (void *)(head))
+
+#define        CIRCLEQ_FIRST(head)     ((head)->cqh_first)
+
+#define        CIRCLEQ_FOREACH(var, head, field)                               \
+       for ((var) = CIRCLEQ_FIRST((head));                             \
+           (var) != (void *)(head);                                    \
+           (var) = CIRCLEQ_NEXT((var), field))
+
+#define        CIRCLEQ_FOREACH_REVERSE(var, head, field)                       \
+       for ((var) = CIRCLEQ_LAST((head));                              \
+           (var) != (void *)(head);                                    \
+           (var) = CIRCLEQ_PREV((var), field))
+
+#define        CIRCLEQ_INIT(head) do {                                         \
+       CIRCLEQ_FIRST((head)) = (void *)(head);                         \
+       CIRCLEQ_LAST((head)) = (void *)(head);                          \
+} while (0)
+
+#define        CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {            \
+       CIRCLEQ_NEXT((elm), field) = CIRCLEQ_NEXT((listelm), field);    \
+       CIRCLEQ_PREV((elm), field) = (listelm);                         \
+       if (CIRCLEQ_NEXT((listelm), field) == (void *)(head))           \
+               CIRCLEQ_LAST((head)) = (elm);                           \
+       else                                                            \
+               CIRCLEQ_PREV(CIRCLEQ_NEXT((listelm), field), field) = (elm);\
+       CIRCLEQ_NEXT((listelm), field) = (elm);                         \
+} while (0)
+
+#define        CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {           \
+       CIRCLEQ_NEXT((elm), field) = (listelm);                         \
+       CIRCLEQ_PREV((elm), field) = CIRCLEQ_PREV((listelm), field);    \
+       if (CIRCLEQ_PREV((listelm), field) == (void *)(head))           \
+               CIRCLEQ_FIRST((head)) = (elm);                          \
+       else                                                            \
+               CIRCLEQ_NEXT(CIRCLEQ_PREV((listelm), field), field) = (elm);\
+       CIRCLEQ_PREV((listelm), field) = (elm);                         \
+} while (0)
+
+#define        CIRCLEQ_INSERT_HEAD(head, elm, field) do {                      \
+       CIRCLEQ_NEXT((elm), field) = CIRCLEQ_FIRST((head));             \
+       CIRCLEQ_PREV((elm), field) = (void *)(head);                    \
+       if (CIRCLEQ_LAST((head)) == (void *)(head))                     \
+               CIRCLEQ_LAST((head)) = (elm);                           \
+       else                                                            \
+               CIRCLEQ_PREV(CIRCLEQ_FIRST((head)), field) = (elm);     \
+       CIRCLEQ_FIRST((head)) = (elm);                                  \
+} while (0)
+
+#define        CIRCLEQ_INSERT_TAIL(head, elm, field) do {                      \
+       CIRCLEQ_NEXT((elm), field) = (void *)(head);                    \
+       CIRCLEQ_PREV((elm), field) = CIRCLEQ_LAST((head));              \
+       if (CIRCLEQ_FIRST((head)) == (void *)(head))                    \
+               CIRCLEQ_FIRST((head)) = (elm);                          \
+       else                                                            \
+               CIRCLEQ_NEXT(CIRCLEQ_LAST((head)), field) = (elm);      \
+       CIRCLEQ_LAST((head)) = (elm);                                   \
+} while (0)
+
+#define        CIRCLEQ_LAST(head)      ((head)->cqh_last)
+
+#define        CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)
+
+#define        CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev)
+
+#define        CIRCLEQ_REMOVE(head, elm, field) do {                           \
+       if (CIRCLEQ_NEXT((elm), field) == (void *)(head))               \
+               CIRCLEQ_LAST((head)) = CIRCLEQ_PREV((elm), field);      \
+       else                                                            \
+               CIRCLEQ_PREV(CIRCLEQ_NEXT((elm), field), field) =       \
+                   CIRCLEQ_PREV((elm), field);                         \
+       if (CIRCLEQ_PREV((elm), field) == (void *)(head))               \
+               CIRCLEQ_FIRST((head)) = CIRCLEQ_NEXT((elm), field);     \
+       else                                                            \
+               CIRCLEQ_NEXT(CIRCLEQ_PREV((elm), field), field) =       \
+                   CIRCLEQ_NEXT((elm), field);                         \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/xen/drivers/scsi/aic7xxx/scsi_iu.h b/xen/drivers/scsi/aic7xxx/scsi_iu.h
new file mode 100644 (file)
index 0000000..801da0a
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * This file is in the public domain.
+ */
+#ifndef        _SCSI_SCSI_IU_H
+#define _SCSI_SCSI_IU_H 1
+
+struct scsi_status_iu_header
+{
+       u_int8_t reserved[2];
+       u_int8_t flags;
+#define        SIU_SNSVALID 0x2
+#define        SIU_RSPVALID 0x1
+       u_int8_t status;
+       u_int8_t sense_length[4];
+       u_int8_t pkt_failures_length[4];
+       u_int8_t pkt_failures[1];
+};
+
+#define SIU_PKTFAIL_OFFSET(siu) 12
+#define SIU_PKTFAIL_CODE(siu) (scsi_4btoul((siu)->pkt_failures) & 0xFF)
+#define                SIU_PFC_NONE                    0
+#define                SIU_PFC_CIU_FIELDS_INVALID      2
+#define                SIU_PFC_TMF_NOT_SUPPORTED       4
+#define                SIU_PFC_TMF_FAILED              5
+#define                SIU_PFC_INVALID_TYPE_CODE       6
+#define                SIU_PFC_ILLEGAL_REQUEST         7
+#define SIU_SENSE_OFFSET(siu)                          \
+    (12 + (((siu)->flags & SIU_RSPVALID)               \
+       ? scsi_4btoul((siu)->pkt_failures_length)       \
+       : 0))
+#endif /*_SCSI_SCSI_IU_H*/
diff --git a/xen/drivers/scsi/aic7xxx/scsi_message.h b/xen/drivers/scsi/aic7xxx/scsi_message.h
new file mode 100644 (file)
index 0000000..75811e2
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is in the public domain.
+ * $FreeBSD: src/sys/cam/scsi/scsi_message.h,v 1.2 2000/05/01 20:21:29 peter Exp $
+ */
+
+/* Messages (1 byte) */                     /* I/T (M)andatory or (O)ptional */
+#define MSG_CMDCOMPLETE                0x00 /* M/M */
+#define MSG_TASK_COMPLETE      0x00 /* M/M */ /* SPI3 Terminology */
+#define MSG_EXTENDED           0x01 /* O/O */
+#define MSG_SAVEDATAPOINTER    0x02 /* O/O */
+#define MSG_RESTOREPOINTERS    0x03 /* O/O */
+#define MSG_DISCONNECT         0x04 /* O/O */
+#define MSG_INITIATOR_DET_ERR  0x05 /* M/M */
+#define MSG_ABORT              0x06 /* O/M */
+#define MSG_ABORT_TASK_SET     0x06 /* O/M */ /* SPI3 Terminology */
+#define MSG_MESSAGE_REJECT     0x07 /* M/M */
+#define MSG_NOOP               0x08 /* M/M */
+#define MSG_PARITY_ERROR       0x09 /* M/M */
+#define MSG_LINK_CMD_COMPLETE  0x0a /* O/O */
+#define MSG_LINK_CMD_COMPLETEF 0x0b /* O/O */
+#define MSG_BUS_DEV_RESET      0x0c /* O/M */
+#define MSG_TARGET_RESET       0x0c /* O/M */ /* SPI3 Terminology */
+#define MSG_ABORT_TAG          0x0d /* O/O */
+#define MSG_ABORT_TASK         0x0d /* O/O */ /* SPI3 Terminology */
+#define MSG_CLEAR_QUEUE                0x0e /* O/O */
+#define MSG_CLEAR_TASK_SET     0x0e /* O/O */ /* SPI3 Terminology */
+#define MSG_INIT_RECOVERY      0x0f /* O/O */ /* Deprecated in SPI3 */
+#define MSG_REL_RECOVERY       0x10 /* O/O */ /* Deprecated in SPI3 */
+#define MSG_TERM_IO_PROC       0x11 /* O/O */ /* Deprecated in SPI3 */
+#define MSG_CLEAR_ACA          0x16 /* O/O */ /* SPI3 */
+#define MSG_LOGICAL_UNIT_RESET 0x17 /* O/O */ /* SPI3 */
+#define MSG_QAS_REQUEST                0x55 /* O/O */ /* SPI3 */
+
+/* Messages (2 byte) */
+#define MSG_SIMPLE_Q_TAG       0x20 /* O/O */
+#define MSG_SIMPLE_TASK                0x20 /* O/O */ /* SPI3 Terminology */
+#define MSG_HEAD_OF_Q_TAG      0x21 /* O/O */
+#define MSG_HEAD_OF_QUEUE_TASK 0x21 /* O/O */ /* SPI3 Terminology */
+#define MSG_ORDERED_Q_TAG      0x22 /* O/O */
+#define MSG_ORDERED_TASK       0x22 /* O/O */ /* SPI3 Terminology */
+#define MSG_IGN_WIDE_RESIDUE   0x23 /* O/O */
+#define MSG_ACA_TASK           0x24 /* 0/0 */ /* SPI3 */
+
+/* Identify message */              /* M/M */  
+#define MSG_IDENTIFYFLAG       0x80 
+#define MSG_IDENTIFY_DISCFLAG  0x40 
+#define MSG_IDENTIFY(lun, disc)        (((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun))
+#define MSG_ISIDENTIFY(m)      ((m) & MSG_IDENTIFYFLAG)
+#define MSG_IDENTIFY_LUNMASK   0x3F 
+
+/* Extended messages (opcode and length) */
+#define MSG_EXT_SDTR           0x01
+#define MSG_EXT_SDTR_LEN       0x03
+
+#define MSG_EXT_WDTR           0x03
+#define MSG_EXT_WDTR_LEN       0x02
+#define MSG_EXT_WDTR_BUS_8_BIT 0x00
+#define MSG_EXT_WDTR_BUS_16_BIT        0x01
+#define MSG_EXT_WDTR_BUS_32_BIT        0x02 /* Deprecated in SPI3 */
+
+#define MSG_EXT_PPR            0x04 /* SPI3 */
+#define MSG_EXT_PPR_LEN                0x06
+#define        MSG_EXT_PPR_PCOMP_EN    0x80
+#define        MSG_EXT_PPR_RTI         0x40
+#define        MSG_EXT_PPR_RD_STRM     0x20
+#define        MSG_EXT_PPR_WR_FLOW     0x10
+#define        MSG_EXT_PPR_HOLD_MCS    0x08
+#define        MSG_EXT_PPR_QAS_REQ     0x04
+#define        MSG_EXT_PPR_DT_REQ      0x02
+#define MSG_EXT_PPR_IU_REQ     0x01
diff --git a/xen/include/asm-i386/string.h b/xen/include/asm-i386/string.h
new file mode 100644 (file)
index 0000000..385da59
--- /dev/null
@@ -0,0 +1,500 @@
+#ifndef _I386_STRING_H_
+#define _I386_STRING_H_
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+/*
+ * On a 486 or Pentium, we are better off not using the
+ * byte string operations. But on a 386 or a PPro the
+ * byte string ops are faster than doing it by hand
+ * (MUCH faster on a Pentium).
+ *
+ * Also, the byte strings actually work correctly. Forget
+ * the i486 routines for now as they may be broken..
+ */
+#if FIXED_486_STRING && defined(CONFIG_X86_USE_STRING_486)
+#include <asm/string-486.h>
+#else
+
+/*
+ * This string-include defines all string functions as inline
+ * functions. Use gcc. It also assumes ds=es=data space, this should be
+ * normal. Most of the string-functions are rather heavily hand-optimized,
+ * see especially strtok,strstr,str[c]spn. They should work, but are not
+ * very easy to understand. Everything is done entirely within the register
+ * set, making the functions fast and clean. String instructions have been
+ * used through-out, making for "slightly" unclear code :-)
+ *
+ *             NO Copyright (C) 1991, 1992 Linus Torvalds,
+ *             consider these trivial functions to be PD.
+ */
+
+
+#define __HAVE_ARCH_STRCPY
+static inline char * strcpy(char * dest,const char *src)
+{
+int d0, d1, d2;
+__asm__ __volatile__(
+       "1:\tlodsb\n\t"
+       "stosb\n\t"
+       "testb %%al,%%al\n\t"
+       "jne 1b"
+       : "=&S" (d0), "=&D" (d1), "=&a" (d2)
+       :"0" (src),"1" (dest) : "memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRNCPY
+static inline char * strncpy(char * dest,const char *src,size_t count)
+{
+int d0, d1, d2, d3;
+__asm__ __volatile__(
+       "1:\tdecl %2\n\t"
+       "js 2f\n\t"
+       "lodsb\n\t"
+       "stosb\n\t"
+       "testb %%al,%%al\n\t"
+       "jne 1b\n\t"
+       "rep\n\t"
+       "stosb\n"
+       "2:"
+       : "=&S" (d0), "=&D" (d1), "=&c" (d2), "=&a" (d3)
+       :"0" (src),"1" (dest),"2" (count) : "memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRCAT
+static inline char * strcat(char * dest,const char * src)
+{
+int d0, d1, d2, d3;
+__asm__ __volatile__(
+       "repne\n\t"
+       "scasb\n\t"
+       "decl %1\n"
+       "1:\tlodsb\n\t"
+       "stosb\n\t"
+       "testb %%al,%%al\n\t"
+       "jne 1b"
+       : "=&S" (d0), "=&D" (d1), "=&a" (d2), "=&c" (d3)
+       : "0" (src), "1" (dest), "2" (0), "3" (0xffffffff):"memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRNCAT
+static inline char * strncat(char * dest,const char * src,size_t count)
+{
+int d0, d1, d2, d3;
+__asm__ __volatile__(
+       "repne\n\t"
+       "scasb\n\t"
+       "decl %1\n\t"
+       "movl %8,%3\n"
+       "1:\tdecl %3\n\t"
+       "js 2f\n\t"
+       "lodsb\n\t"
+       "stosb\n\t"
+       "testb %%al,%%al\n\t"
+       "jne 1b\n"
+       "2:\txorl %2,%2\n\t"
+       "stosb"
+       : "=&S" (d0), "=&D" (d1), "=&a" (d2), "=&c" (d3)
+       : "0" (src),"1" (dest),"2" (0),"3" (0xffffffff), "g" (count)
+       : "memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRCMP
+static inline int strcmp(const char * cs,const char * ct)
+{
+int d0, d1;
+register int __res;
+__asm__ __volatile__(
+       "1:\tlodsb\n\t"
+       "scasb\n\t"
+       "jne 2f\n\t"
+       "testb %%al,%%al\n\t"
+       "jne 1b\n\t"
+       "xorl %%eax,%%eax\n\t"
+       "jmp 3f\n"
+       "2:\tsbbl %%eax,%%eax\n\t"
+       "orb $1,%%al\n"
+       "3:"
+       :"=a" (__res), "=&S" (d0), "=&D" (d1)
+                    :"1" (cs),"2" (ct));
+return __res;
+}
+
+#define __HAVE_ARCH_STRNCMP
+static inline int strncmp(const char * cs,const char * ct,size_t count)
+{
+register int __res;
+int d0, d1, d2;
+__asm__ __volatile__(
+       "1:\tdecl %3\n\t"
+       "js 2f\n\t"
+       "lodsb\n\t"
+       "scasb\n\t"
+       "jne 3f\n\t"
+       "testb %%al,%%al\n\t"
+       "jne 1b\n"
+       "2:\txorl %%eax,%%eax\n\t"
+       "jmp 4f\n"
+       "3:\tsbbl %%eax,%%eax\n\t"
+       "orb $1,%%al\n"
+       "4:"
+                    :"=a" (__res), "=&S" (d0), "=&D" (d1), "=&c" (d2)
+                    :"1" (cs),"2" (ct),"3" (count));
+return __res;
+}
+
+#define __HAVE_ARCH_STRCHR
+static inline char * strchr(const char * s, int c)
+{
+int d0;
+register char * __res;
+__asm__ __volatile__(
+       "movb %%al,%%ah\n"
+       "1:\tlodsb\n\t"
+       "cmpb %%ah,%%al\n\t"
+       "je 2f\n\t"
+       "testb %%al,%%al\n\t"
+       "jne 1b\n\t"
+       "movl $1,%1\n"
+       "2:\tmovl %1,%0\n\t"
+       "decl %0"
+       :"=a" (__res), "=&S" (d0) : "1" (s),"0" (c));
+return __res;
+}
+
+#define __HAVE_ARCH_STRRCHR
+static inline char * strrchr(const char * s, int c)
+{
+int d0, d1;
+register char * __res;
+__asm__ __volatile__(
+       "movb %%al,%%ah\n"
+       "1:\tlodsb\n\t"
+       "cmpb %%ah,%%al\n\t"
+       "jne 2f\n\t"
+       "leal -1(%%esi),%0\n"
+       "2:\ttestb %%al,%%al\n\t"
+       "jne 1b"
+       :"=g" (__res), "=&S" (d0), "=&a" (d1) :"0" (0),"1" (s),"2" (c));
+return __res;
+}
+
+#define __HAVE_ARCH_STRLEN
+static inline size_t strlen(const char * s)
+{
+int d0;
+register int __res;
+__asm__ __volatile__(
+       "repne\n\t"
+       "scasb\n\t"
+       "notl %0\n\t"
+       "decl %0"
+       :"=c" (__res), "=&D" (d0) :"1" (s),"a" (0), "0" (0xffffffff));
+return __res;
+}
+
+static inline void * __memcpy(void * to, const void * from, size_t n)
+{
+int d0, d1, d2;
+__asm__ __volatile__(
+       "rep ; movsl\n\t"
+       "testb $2,%b4\n\t"
+       "je 1f\n\t"
+       "movsw\n"
+       "1:\ttestb $1,%b4\n\t"
+       "je 2f\n\t"
+       "movsb\n"
+       "2:"
+       : "=&c" (d0), "=&D" (d1), "=&S" (d2)
+       :"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from)
+       : "memory");
+return (to);
+}
+
+/*
+ * This looks horribly ugly, but the compiler can optimize it totally,
+ * as the count is constant.
+ */
+static inline void * __constant_memcpy(void * to, const void * from, size_t n)
+{
+       switch (n) {
+               case 0:
+                       return to;
+               case 1:
+                       *(unsigned char *)to = *(const unsigned char *)from;
+                       return to;
+               case 2:
+                       *(unsigned short *)to = *(const unsigned short *)from;
+                       return to;
+               case 3:
+                       *(unsigned short *)to = *(const unsigned short *)from;
+                       *(2+(unsigned char *)to) = *(2+(const unsigned char *)from);
+                       return to;
+               case 4:
+                       *(unsigned long *)to = *(const unsigned long *)from;
+                       return to;
+               case 6: /* for Ethernet addresses */
+                       *(unsigned long *)to = *(const unsigned long *)from;
+                       *(2+(unsigned short *)to) = *(2+(const unsigned short *)from);
+                       return to;
+               case 8:
+                       *(unsigned long *)to = *(const unsigned long *)from;
+                       *(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+                       return to;
+               case 12:
+                       *(unsigned long *)to = *(const unsigned long *)from;
+                       *(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+                       *(2+(unsigned long *)to) = *(2+(const unsigned long *)from);
+                       return to;
+               case 16:
+                       *(unsigned long *)to = *(const unsigned long *)from;
+                       *(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+                       *(2+(unsigned long *)to) = *(2+(const unsigned long *)from);
+                       *(3+(unsigned long *)to) = *(3+(const unsigned long *)from);
+                       return to;
+               case 20:
+                       *(unsigned long *)to = *(const unsigned long *)from;
+                       *(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+                       *(2+(unsigned long *)to) = *(2+(const unsigned long *)from);
+                       *(3+(unsigned long *)to) = *(3+(const unsigned long *)from);
+                       *(4+(unsigned long *)to) = *(4+(const unsigned long *)from);
+                       return to;
+       }
+#define COMMON(x) \
+__asm__ __volatile__( \
+       "rep ; movsl" \
+       x \
+       : "=&c" (d0), "=&D" (d1), "=&S" (d2) \
+       : "0" (n/4),"1" ((long) to),"2" ((long) from) \
+       : "memory");
+{
+       int d0, d1, d2;
+       switch (n % 4) {
+               case 0: COMMON(""); return to;
+               case 1: COMMON("\n\tmovsb"); return to;
+               case 2: COMMON("\n\tmovsw"); return to;
+               default: COMMON("\n\tmovsw\n\tmovsb"); return to;
+       }
+}
+  
+#undef COMMON
+}
+
+#define __HAVE_ARCH_MEMCPY
+
+#define memcpy(t, f, n) \
+(__builtin_constant_p(n) ? \
+ __constant_memcpy((t),(f),(n)) : \
+ __memcpy((t),(f),(n)))
+
+
+/*
+ * struct_cpy(x,y), copy structure *x into (matching structure) *y.
+ *
+ * We get link-time errors if the structure sizes do not match.
+ * There is no runtime overhead, it's all optimized away at
+ * compile time.
+ */
+//extern void __struct_cpy_bug (void);
+
+/*
+#define struct_cpy(x,y)                        \
+({                                             \
+       if (sizeof(*(x)) != sizeof(*(y)))       \
+               __struct_cpy_bug;               \
+       memcpy(x, y, sizeof(*(x)));             \
+})
+*/
+
+#define __HAVE_ARCH_MEMMOVE
+static inline void * memmove(void * dest,const void * src, size_t n)
+{
+int d0, d1, d2;
+if (dest<src)
+__asm__ __volatile__(
+       "rep\n\t"
+       "movsb"
+       : "=&c" (d0), "=&S" (d1), "=&D" (d2)
+       :"0" (n),"1" (src),"2" (dest)
+       : "memory");
+else
+__asm__ __volatile__(
+       "std\n\t"
+       "rep\n\t"
+       "movsb\n\t"
+       "cld"
+       : "=&c" (d0), "=&S" (d1), "=&D" (d2)
+       :"0" (n),
+        "1" (n-1+(const char *)src),
+        "2" (n-1+(char *)dest)
+       :"memory");
+return dest;
+}
+
+#define memcmp __builtin_memcmp
+
+#define __HAVE_ARCH_MEMCHR
+static inline void * memchr(const void * cs,int c,size_t count)
+{
+int d0;
+register void * __res;
+if (!count)
+       return NULL;
+__asm__ __volatile__(
+       "repne\n\t"
+       "scasb\n\t"
+       "je 1f\n\t"
+       "movl $1,%0\n"
+       "1:\tdecl %0"
+       :"=D" (__res), "=&c" (d0) : "a" (c),"0" (cs),"1" (count));
+return __res;
+}
+
+static inline void * __memset_generic(void * s, char c,size_t count)
+{
+int d0, d1;
+__asm__ __volatile__(
+       "rep\n\t"
+       "stosb"
+       : "=&c" (d0), "=&D" (d1)
+       :"a" (c),"1" (s),"0" (count)
+       :"memory");
+return s;
+}
+
+/* we might want to write optimized versions of these later */
+#define __constant_count_memset(s,c,count) __memset_generic((s),(c),(count))
+
+/*
+ * memset(x,0,y) is a reasonably common thing to do, so we want to fill
+ * things 32 bits at a time even when we don't know the size of the
+ * area at compile-time..
+ */
+static inline void * __constant_c_memset(void * s, unsigned long c, size_t count)
+{
+int d0, d1;
+__asm__ __volatile__(
+       "rep ; stosl\n\t"
+       "testb $2,%b3\n\t"
+       "je 1f\n\t"
+       "stosw\n"
+       "1:\ttestb $1,%b3\n\t"
+       "je 2f\n\t"
+       "stosb\n"
+       "2:"
+       : "=&c" (d0), "=&D" (d1)
+       :"a" (c), "q" (count), "0" (count/4), "1" ((long) s)
+       :"memory");
+return (s);    
+}
+
+/* Added by Gertjan van Wingerde to make minix and sysv module work */
+#define __HAVE_ARCH_STRNLEN
+static inline size_t strnlen(const char * s, size_t count)
+{
+int d0;
+register int __res;
+__asm__ __volatile__(
+       "movl %2,%0\n\t"
+       "jmp 2f\n"
+       "1:\tcmpb $0,(%0)\n\t"
+       "je 3f\n\t"
+       "incl %0\n"
+       "2:\tdecl %1\n\t"
+       "cmpl $-1,%1\n\t"
+       "jne 1b\n"
+       "3:\tsubl %2,%0"
+       :"=a" (__res), "=&d" (d0)
+       :"c" (s),"1" (count));
+return __res;
+}
+/* end of additional stuff */
+
+//#define __HAVE_ARCH_STRSTR
+
+//extern char *strstr(const char *cs, const char *ct);
+
+/*
+ * This looks horribly ugly, but the compiler can optimize it totally,
+ * as we by now know that both pattern and count is constant..
+ */
+static inline void * __constant_c_and_count_memset(void * s, unsigned long pattern, size_t count)
+{
+       switch (count) {
+               case 0:
+                       return s;
+               case 1:
+                       *(unsigned char *)s = pattern;
+                       return s;
+               case 2:
+                       *(unsigned short *)s = pattern;
+                       return s;
+               case 3:
+                       *(unsigned short *)s = pattern;
+                       *(2+(unsigned char *)s) = pattern;
+                       return s;
+               case 4:
+                       *(unsigned long *)s = pattern;
+                       return s;
+       }
+#define COMMON(x) \
+__asm__  __volatile__( \
+       "rep ; stosl" \
+       x \
+       : "=&c" (d0), "=&D" (d1) \
+       : "a" (pattern),"0" (count/4),"1" ((long) s) \
+       : "memory")
+{
+       int d0, d1;
+       switch (count % 4) {
+               case 0: COMMON(""); return s;
+               case 1: COMMON("\n\tstosb"); return s;
+               case 2: COMMON("\n\tstosw"); return s;
+               default: COMMON("\n\tstosw\n\tstosb"); return s;
+       }
+}
+  
+#undef COMMON
+}
+
+#define __constant_c_x_memset(s, c, count) \
+(__builtin_constant_p(count) ? \
+ __constant_c_and_count_memset((s),(c),(count)) : \
+ __constant_c_memset((s),(c),(count)))
+
+#define __memset(s, c, count) \
+(__builtin_constant_p(count) ? \
+ __constant_count_memset((s),(c),(count)) : \
+ __memset_generic((s),(c),(count)))
+
+#define __HAVE_ARCH_MEMSET
+#define memset(s, c, count) \
+(__builtin_constant_p(c) ? \
+ __constant_c_x_memset((s),(0x01010101UL*(unsigned char)(c)),(count)) : \
+ __memset((s),(c),(count)))
+
+/*
+ * find the first occurrence of byte 'c', or 1 past the area if none
+ */
+#define __HAVE_ARCH_MEMSCAN
+static inline void * memscan(void * addr, int c, size_t size)
+{
+       if (!size)
+               return addr;
+       __asm__("repnz; scasb\n\t"
+               "jnz 1f\n\t"
+               "dec %%edi\n"
+               "1:"
+               : "=D" (addr), "=c" (size)
+               : "0" (addr), "1" (size), "a" (c));
+       return addr;
+}
+
+#endif /* CONFIG_X86_USE_STRING_486 */
+#endif /* __KERNEL__ */
+
+#endif
index cd40d119f00ec7bc1d28ee45787f68ad6bbbafb6..a9eeda6716bb900cb5772efd603656494554c7bd 100644 (file)
@@ -14,6 +14,9 @@
 printk("Memory Reservation 0x%lx, %lu bytes\n", (_p), (_l))
 
 /* lib.c */
+#include <xeno/string.h>
+
+/* JWS - pulled over linux string library ({asm,linux}/string.h)
 int memcmp(const void * cs,const void * ct,size_t count);
 void * memcpy(void * dest,const void *src,size_t count);
 int strncmp(const char * cs,const char * ct,size_t count);
@@ -25,6 +28,8 @@ size_t strnlen(const char * s, size_t count);
 size_t strlen(const char * s);
 char * strchr(const char *,int);
 char * strstr(const char * s1,const char * s2);
+*/
+
 unsigned long str_to_quad(unsigned char *s);
 unsigned char *quad_to_str(unsigned long q, unsigned char *s);
 
@@ -46,6 +51,7 @@ extern int sscanf(const char *, const char *, ...)
        __attribute__ ((format (scanf,2,3)));
 extern int vsscanf(const char *, const char *, va_list);
 long simple_strtol(const char *cp,char **endp,unsigned int base);
+unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base);
 long long simple_strtoll(const char *cp,char **endp,unsigned int base);
 
 #endif /* __LIB_H__ */
diff --git a/xen/include/xeno/string.h b/xen/include/xeno/string.h
new file mode 100644 (file)
index 0000000..71ab1c4
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef _LINUX_STRING_H_
+#define _LINUX_STRING_H_
+
+/* We don't want strings.h stuff being user by user stuff by accident */
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>       /* for size_t */
+//#include <linux/stddef.h>    /* for NULL */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define __kernel_size_t size_t
+
+extern char * ___strtok;
+extern char * strpbrk(const char *,const char *);
+extern char * strtok(char *,const char *);
+extern char * strsep(char **,const char *);
+extern __kernel_size_t strspn(const char *,const char *);
+
+
+/*
+ * Include machine specific inline routines
+ */
+#include <asm/string.h>
+
+#ifndef __HAVE_ARCH_STRCPY
+extern char * strcpy(char *,const char *);
+#endif
+#ifndef __HAVE_ARCH_STRNCPY
+extern char * strncpy(char *,const char *, __kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_STRCAT
+extern char * strcat(char *, const char *);
+#endif
+#ifndef __HAVE_ARCH_STRNCAT
+extern char * strncat(char *, const char *, __kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_STRCMP
+extern int strcmp(const char *,const char *);
+#endif
+#ifndef __HAVE_ARCH_STRNCMP
+extern int strncmp(const char *,const char *,__kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_STRNICMP
+extern int strnicmp(const char *, const char *, __kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_STRCHR
+extern char * strchr(const char *,int);
+#endif
+#ifndef __HAVE_ARCH_STRRCHR
+extern char * strrchr(const char *,int);
+#endif
+#ifndef __HAVE_ARCH_STRSTR
+extern char * strstr(const char *,const char *);
+#endif
+#ifndef __HAVE_ARCH_STRLEN
+extern __kernel_size_t strlen(const char *);
+#endif
+#ifndef __HAVE_ARCH_STRNLEN
+extern __kernel_size_t strnlen(const char *,__kernel_size_t);
+#endif
+
+#ifndef __HAVE_ARCH_MEMSET
+extern void * memset(void *,int,__kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_MEMCPY
+extern void * memcpy(void *,const void *,__kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_MEMMOVE
+extern void * memmove(void *,const void *,__kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_MEMSCAN
+extern void * memscan(void *,int,__kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_MEMCMP
+extern int memcmp(const void *,const void *,__kernel_size_t);
+#endif
+#ifndef __HAVE_ARCH_MEMCHR
+extern void * memchr(const void *,int,__kernel_size_t);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+#endif /* _LINUX_STRING_H_ */